5

我想枚举一些整数因子的所有可能乘积,只达到某个最大值:

  • P((2, 3, 11), 10)会回来(2, 3, 4, 6, 8, 9)的。
  • P((5, 7, 13), 30)会回来(5, 7, 13, 25)的。

这似乎是一个树遍历,一旦达到最大值,树枝就会停止生长,但我不知道树枝数量的界限是多少。对于这个问题,推荐什么算法或习语?到目前为止我看到的最接近的是itertools.product(),它似乎为每个输出集设置了固定数量的术语(例如 2)。

对于上下文,我正在尝试检查与 n 互质的数字。在这种情况下,n 本身是上限,因子列表是 n 的因子。我试图概括上面的问题。

4

3 回答 3

3

我喜欢这种方法,它涉及将输入列表中的所有元素乘以 1,然后将所有结果乘以输入列表中的元素,等等,直到达到限制。

def signature_seq(signature, limit):
  products = set((1,))
  for factor in signature:
    new_products = set()
    for prod in products:
      x = factor * prod
      while x <= limit:
        new_products.add(x)
        x *= factor
    products.update(new_products)

  products.remove(1)
  return products

这应该做你想要的:

>>> print(sorted(signature_seq((2, 3, 11), 10)))
[2, 3, 4, 6, 8, 9]
>>> print(sorted(signature_seq((5, 7, 13), 30)))
[5, 7, 13, 25]

顺便说一句,如果给定一个从 2 开始的连续素数列表,这是一个平滑数生成器。

于 2013-07-25T01:13:50.013 回答
2

这是使用生成器(和itertools.count)的解决方案:

from itertools import count

def products(numbers, limit):
    numbers = set(numbers)  # needs a set to pop from, not a tuple
    while numbers:
        n = numbers.pop()
        for r in (n ** e for e in count(1)):
            if r > limit:
                break
            yield r
            for p in products(numbers, limit / r):
                yield r * p

因为它是一个生成器,所以它返回一个迭代器——结果没有排序,所以对于你想要的特定输出,你可以这样称呼它:

>>> sorted(products((2, 3, 11), 10))
[2, 3, 4, 6, 8, 9]
>>> sorted(products((5, 7, 13), 30))
[5, 7, 13, 25]
于 2013-07-25T01:33:29.123 回答
0

仅与元组 (2,3,4) 一起使用的想法itertools,例如:

有多个笛卡尔积:

(2,),(3,),(4,) # repeat 1
(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4) # repeat 2
...

对于每个元组,使用reducewithoperator.mul和起始值 1 将它们相乘:

reduce(operator.mul, tuple, 1)

这将产生 2 级笛卡尔积的乘法:

[reduce(operator.mul,t,3) for t in itertools.product((1,2,3),repeat=2)]
>>>[3, 6, 9, 6, 12, 18, 9, 18, 27]

现在,我们需要增加repeat直到满足停止条件,例如:每次乘法都会产生大于top. 由于计数的最小值是2(因为1多次1只是 1,所以它不会计数)我们可以将 x 乘以 2 而低于top。所以: top/2 = x, 意味着我们可以遍历range(1,top/2):

[reduce(operator.mul,t,1) for c in range(1,10/2) for t in itertools.product((1,2,3),repeat=2) if reduce(operator.mul, t, 1) < 10]

这将产生重复的值,所以让我们将它们转换为一组:

set([reduce(operator.mul,t,1) for c in range(1,10/2) for t in itertools.product((1,2,3),repeat=2) if reduce(operator.mul, t, 1) < 10])

仅使用itertools可能会很麻烦,但解决方案似乎很漂亮。我确信可以通过引入更好的停止条件来优化它。最终代码如下所示:

注意:有一个素数定理可以让您优化停止条件以math.sqrt(top)

import math
def f(t,m):
    return set([reduce(operator.mul, t1, 1) for c in range(1,int(math.sqrt(m)) for t1 in itertools.product(t,repeat=c) if reduce(operator.mul,t1,1) < m])

f((2,3,4),10)
>>>set([2, 3, 4, 6, 8, 9])

希望这可能会给你另一个想法:)

于 2013-07-25T15:20:36.147 回答