目前,我正在从列表和该子集的产品中输出所有素数组合,如下所示:
from operator import mul
from itertools import combinations
primes = [2, 3, 5, 7, 11]
for r in range(1,len(primes)):
for combo in combinations(primes,r+1):
print combo, reduce(mul, combo)
哪个输出
(2,) 2
(3,) 3
(5,) 5
(7,) 7
(11,) 11
(2, 3) 6
(2, 5) 10
(2, 7) 14
(2, 11) 22
(3, 5) 15
(3, 7) 21
(3, 11) 33
(5, 7) 35
(5, 11) 55
(7, 11) 77
(2, 3, 5) 30
(2, 3, 7) 42
(2, 3, 11) 66
(2, 5, 7) 70
(2, 5, 11) 110
(2, 7, 11) 154
(3, 5, 7) 105
(3, 5, 11) 165
(3, 7, 11) 231
(5, 7, 11) 385
(2, 3, 5, 7) 210
(2, 3, 5, 11) 330
(2, 3, 7, 11) 462
(2, 5, 7, 11) 770
(3, 5, 7, 11) 1155
(2, 3, 5, 7, 11) 2310
现在让我们说我们正在看下面的块:
(2, 5, 7) 70
(2, 5, 11) 110
(2, 7, 11) 154
(3, 5, 7) 105
(3, 5, 11) 165
(3, 7, 11) 231
(5, 7, 11) 385
(2, 3, 5, 7) 210
(2, 3, 5, 11) 330
为了举例,我想迭代产品<110的所有组合。这里的第一个“终点”出现在(2,5,11),因为产品是110。
问题在于,如果此时中断,它会认为我试图打破所有长度为3的元组并继续(2,3,5,7),从而跳过其他有效(3,5,7),其产品<110。另一方面,如果我只是在这一点上做继续,我最终会反复通过大量的元组,我知道这将浪费时间。如果我知道(2,5,11)太大,那么(2,7,11)显然也会太大,我不应该对它进行评估。
我不确定我的问题是否清楚,但是还有另一种方法来生成组合,其中输出的顺序更符合我的结构吗?
答案 0 :(得分:1)
“问题”是combinations
生成器按特定顺序发出长度为r
的所有元组。你希望他们有不同的顺序。因此,您需要编写自己的combinations
生成器。
这些类型的生成器通常以递归方式编写:从r
发出所有i
- 长度组合,首先删除一个元素 - 第一个 - 然后以递归方式发出所有(r-1)
其余部分的长度组合。
现在,您想要做的是在产品太大时停止递归,这样您就不会发出不必要的元组。不幸的是,编写这样一个生成器的通常方法不允许你这样做,因为词典顺序与“按产品增加的顺序”不一样。
这意味着您必须找到一种不同的递归方式,每次都会增加数字的乘积。一点想法应该带你到以下算法:
这可以实现如下。
from operator import mul
primes = [2, 3, 5, 7, 11]
primes_inorder = dict(zip(primes, primes[1:]))
def my_combinations(primes, r, N, start=None, prod=None):
"""Yield all sorted combinations of length `r`
from the sorted list `primes`."""
if start is None:
start = primes[:r]
prod = reduce(mul, start)
if prod > N: return
yield start
for i, v in enumerate(start):
next_v = primes_inorder.get(v, None)
if next_v is None or (i+1 < r and next_v > start[i+1]):
continue
new_prod = prod / v * next_v
if new_prod > N:
continue
new_start = start[:]
new_start[i] = next_v
for combination in my_combinations(primes, r, N, start=new_start, prod=new_prod):
yield combination