Python中做笛卡尔积,最好用生成器表达式进行遍历,而不要一下子强转成list

算法中经常需要做笛卡尔积。笛卡尔积结果的数据量会随着参与运算的list的数量成类似于指数的变化。

比如,100个只有两个元素的list做笛卡尔积,其结果的数据量会有多大呢?

答案是2的100次方个元素,这个数字非常大。想想那个用麦粒放棋盘的著名寓言吧。

因此,在实际应用笛卡尔积时要特别小心,否则它有可能是灾难性的。

算法的实现通常用python来实现。在Python中计算笛卡尔积的语法很简单。

以下是一个示例代码段:

import itertools

list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
list3 = ['X', 'Y', 'Z']

products = list(itertools.product(list1, list2, list3))

for i in products:
    do some thing

上面的代码中,products 的值如下:

[(1, 'a', 'X'), (1, 'a', 'Y'), (1, 'a', 'Z'), 
(1, 'b', 'X'), (1, 'b', 'Y'), (1, 'b', 'Z'), 
(1, 'c', 'X'), (1, 'c', 'Y'), (1, 'c', 'Z'), 
(2, 'a', 'X'), (2, 'a', 'Y'), (2, 'a', 'Z'),
(2, 'b', 'X'), (2, 'b', 'Y'), (2, 'b', 'Z'), 
(2, 'c', 'X'), (2, 'c', 'Y'), (2, 'c', 'Z'), 
(3, 'a', 'X'), (3, 'a', 'Y'), (3, 'a', 'Z'), 
(3, 'b', 'X'), (3, 'b', 'Y'), (3, 'b', 'Z'), 
(3, 'c', 'X'), (3, 'c', 'Y'), (3, 'c', 'Z')]

可以看出来,即使就是这样简简单单的3个list进行笛卡尔积,得到的结果已经是一个挺长的结果了。

所以在写程序的时候,下面三点很重要。可能只需做一点改变,就会使程序运行的快上几,几十倍。

第一点是,在笛卡尔积之前,就对原始数据进行筛选,不要让一些一定不需要的数据参与运算。这将大大减少后续的运算量,也会减少内存的减少。

第二点是,在对笛卡尔积进行运算得到结果以后,要找到合适的逻辑,避免遍历结果中的每一个元素,否则你的程序可能会跑上几个月也并不夸张。

第三点,是要着重介绍的。

上面的示例程序中,在进行笛卡尔积的同时,将数据转化成了list数据,赋值给了products。

这在很多情况下是一个好的编程习惯,很多算法工程师也是这样应用的,这会使程序看起来更加简洁。

但是,这里可能会使程序遇到大麻烦!

这是因为如果参与运算的入参数据量比较大(其实看起来也并不是很大,就像上面说的),你得到的list将可能是一个天文数字,即使是服务器几百个G的内存,也将瞬间被占满,程序将卡死在这里。

其实解决这个问题很简单。

itertools.product是一个生成器表达式。它将给定的可迭代对象的笛卡尔积作为元组逐个生成,而不是一次性生成并存储在内存中。因此在运行itertools.product(list1, list2, list3)时并没有把最终结果生成出来存入内存。单独运行下面这行代码其实很快,并且内存占用几乎可以忽略。

itertools.product(list1, list2, list3)

但是前面加了个list的强转,就像示例程序那样,就相当于要求这个生成器表达式将所有结果运算出来,存入到列表中,放入计算机内存。所以这样的做法很危险。

list(itertools.product(list1, list2, list3))

现实情况是,当面对如此巨大的数据量时,我们的算法往往并不是真的需要所有这些数据来进行接下来的运算,或者作为最终结果输出。接下来往往面对着数据的进一步处理,可能是一个循环,因此在处理时,通过对这个生成器表达式进行处理就可以了。这样,在每次用到笛卡尔积结果中的一个元素时,才会运算得到这个结果,内存的问题得到解决。但应该注意到,这也将是一个超级大的计算量。所以这种情况下,应该找到合适的逻辑,能够尽早的跳出循环。

同样的道理,在其他语言中进行笛卡尔积时,也要注意,在数据量大的时候,不能一下子将结果全部生成出来存到内存中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值