以某种方式迭代素数

时间:2012-04-17 20:01:02

标签: python combinations itertools

目前,我正在从列表和该子集的产品中输出所有素数组合,如下所示:

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)显然也会太大,我不应该对它进行评估。

我不确定我的问题是否清楚,但是还有另一种方法来生成组合,其中输出的顺序更符合我的结构吗?

1 个答案:

答案 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