【问题标题】:Enumeration of all factor products less than a maximum枚举小于最大值的所有因子产品
【发布时间】:2013-07-25 00:51:14
【问题描述】:

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

  • 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 的因子。我试着概括一下上面的问题。

【问题讨论】:

    标签: python algorithm permutation primes factors


    【解决方案1】:

    我喜欢这种方法,它包括将输入列表中的所有元素乘以 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 开始的连续素数列表,这是一个smooth number 生成器。

    【讨论】:

    • 零比雷埃夫斯的算法同样优雅,但这个似乎更快。谢谢各位!!
    【解决方案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]
    

    【讨论】:

      【解决方案3】:

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

      有多个笛卡尔积:

      (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
      ...
      

      对于每个元组,使用 reduceoperator.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])
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-14
        • 2011-10-12
        • 2010-11-10
        • 1970-01-01
        • 2010-09-17
        • 2019-07-21
        • 1970-01-01
        相关资源
        最近更新 更多