1

假设我们有数字因子,例如 1260:

>>> factors(1260)
[2, 2, 3, 3, 5, 7]

在 Python 组合中,与这些数字可能的每个子产品(即所有因子,不仅是素因子,因子之和小于max_product )进行组合的最佳方法是什么?

如果我从主要因素进行组合,我必须重构产品的剩余部分,因为我不知道剩余部分没有组合。

我还可以改进我的除数函数以生成除数对而不是按大小顺序生成除数,但是对于产品高达 12000的数字,我仍然需要这样做。产品必须始终保持不变。

我与除数例程相关联,但看起来不值得努力证明它们适用于我的其他代码。至少我的除数函数明显比 sympy 快:

def divides(number):
    if number<2:
        yield number
        return
    high = [number]
    sqr = int(number ** 0.5)
    limit = sqr+(sqr*sqr != number)
    yield 1
    for divisor in xrange(3, limit, 2) if (number & 1) else xrange(2, limit):
        if not number % divisor:
            yield divisor
            high.append(number//divisor)
    if sqr*sqr== number: yield sqr
    for divisor in reversed(high):
        yield divisor

重用此代码的唯一问题是将除数链接到分解筛或对除数的除数执行某种 itertools.product,我将成对给出,而不是按顺序排序。

示例结果为:

[4, 3, 3, 5, 7] (one replacement of two)
[5, 7, 36] (one replacement of three)
[3, 6, 14, 5] (two replacements)

可能我需要一些方法来为较小的除数生成筛子或动态编程解决方案,这些除数可能与它们的除数相关联。看起来很难避免重叠。我确实准备了一个筛子函数,它为每个数字存储最大的素数,以加快分解速度,而不保存每个数字的完整分解……也许可以调整。

更新:因子的总和应该接近产品,因此答案中可能有大量 <= 10 的因子(最多 14 个因子)。

UPDATE2: 这是我的代码,但必须弄清楚如何递归或迭代地对大于 2 的部分进行多次删除,并挖掘词法分区以替换产生重复的跳跃位模式(可怜的命中计数仅用于一个替换,并且不计算在 single_partition 内传递的“单个元素分区”):

from __future__ import print_function
import itertools
import operator
from euler import factors

def subset(seq, mask):
    """ binary mask of len(seq) bits, return generator for the sequence """
    # this is not lexical order, replace with lexical order masked passing duplicates
    return (c for ind,c in enumerate(seq) if mask & (1<<ind))


def single_partition(seq, n = 0, func = lambda x: x):
    ''' map given function to one partition  '''
    for n in range(n, (2**len(seq))):
        result = tuple(subset(seq,n))
        others = tuple(subset(seq,~n))
        if len(result) < 2 or len(others) == 0:
            #empty subset or only one or all
            continue
        result = (func(result),)+others
        yield result


if __name__=='__main__':
    seen,  hits, count = set(), 0, 0
    for f in single_partition(factors(13824), func = lambda x: reduce(operator.mul, x)):
        if f not in seen:
            print(f,end=' ')
            seen.add(f)
        else:
            hits += 1
        count += 1
    print('\nGenerated %i, hits %i' %(count,hits))

改进我很高兴在非主要因素部分中仅获得最大 5 个因素的因数。我亲手发现最多 5 个相同因子的非递减安排遵循以下形式:

partitions of 5    applied to 2**5
1  1  1   1  1     2  2   2   2  2
1  1  1     2      2  2   2    4
1  1  1  3         2  2      8
1   2       2      2    4      4 
1       4          2      16
  2      3           4       8

解决方案 我不会从好的解决方案中删除已接受的答案,但它对于这项工作来说过于复杂。从 Project Euler 我只揭示了来自 NZ orbifold 的这个辅助函数,它工作得更快,而且不需要首先使用主要因素:

def factorings(n,k=2):
    result = []
    while k*k <= n:
        if n%k == 0:
            result.extend([[k]+f for f in factorings(n/k,k)])
        k += 1
    return result + [[n]]

我的计时装饰器在 4.85 秒内运行 Python 2.7 的问题 88的相关解决方案,并在使用 psyco 的 2.6.6 中找到计数器 3.4 秒优化停止条件后,在没有 psyco 的 2.7 中找到 3.7 秒。在 Python 2.6.6 中,我自己的代码速度从 30 秒(使用接受答案的代码(由我添加的排序删除))到 2.25 秒(2.7 没有 psyco)和782 毫秒(使用 psyco)。

4

3 回答 3

1

我使用[(2, 9), (3, 3)](for [2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3]) 之类的列表作为未扩展因子的基本列表和扩展因子列表。在每一轮中,我从基础中选择一个因子,减少它的计数,然后

  • 将它添加到扩展列表中,增加它的长度,所以我们总共有一个额外的因素(直到袖口)
  • 将它与所有扩展因子相乘,产生所有可能性

使用动态规划和截止策略,这变得非常快:

from itertools import groupby

def multiplicies( factors ):
    """ [x,x,x,,y,y] -> [(x,3), (y,2)] """
    return ((k, sum(1 for _ in group)) for k, group in groupby(factors))

def combinate(facs, cutoff=None):
    facs = tuple(multiplicies(facs))

    results = set()
    def explode(base, expanded):
        # `k` is the key for the caching
        # if the function got called like this before return instantly
        k = (base, expanded)
        if k in results:
            return
        results.add(k)

        # pick a factor
        for (f,m) in base:
            # remove it from the bases
            newb = ((g, (n if g!=f else n-1)) for g,n in base)
            newb = tuple((g,x) for g,x in newb if x > 0)

            # do we cutoff yet?
            if cutoff is None or len(newb) + len(expanded) < cutoff:
                explode(newb, tuple(sorted(expanded + (f,))))

            # multiply the pick with each factor in expanded
            for pos in range(len(expanded)):
                newexp = list(expanded)
                newexp[pos] *= f
                explode(newb, tuple(sorted(newexp)))

    explode(facs, ())
    # turn the `k` (see above) into real factor lists
    return set((tuple((x**y) for x,y in bases) + expanded) 
        for (bases, expanded) in results)

# you dont even need the cutoff here!
combinate([2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3])
# but you need it for 
combinate([2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,5,7,9,11], 5)

如果可以的话,试试Psyco(我不能,因为我这里只有 Py2.7),它也可能会加快速度。

于 2011-03-06T01:09:46.500 回答
1

您要查找的内容通常称为除数这个问题的答案可能会对您有所帮助。

于 2011-03-05T23:22:11.960 回答
1
from __future__ import print_function
import itertools
import operator

def partition(iterable, chain=itertools.chain, map=map):
    # http://code.activestate.com/recipes/576795/
    # In [1]: list(partition('abcd'))
    # Out[1]: 
    # [['abcd'],
    #  ['a', 'bcd'],
    #  ['ab', 'cd'],
    #  ['abc', 'd'],
    #  ['a', 'b', 'cd'],
    #  ['a', 'bc', 'd'],
    #  ['ab', 'c', 'd'],
    #  ['a', 'b', 'c', 'd']]    
    s = iterable if hasattr(iterable, '__getslice__') else tuple(iterable)
    n = len(s)
    first, middle, last = [0], range(1, n), [n]
    getslice = s.__getslice__
    return [map(getslice, chain(first, div), chain(div, last))
            for i in range(n) for div in itertools.combinations(middle, i)]

def product(factors,mul=operator.mul):
    return reduce(mul,factors,1)

def factorings(factors,product=product,
               permutations=itertools.permutations,
               imap=itertools.imap,
               chain_from_iterable=itertools.chain.from_iterable,
               ):
    seen=set()
    seen.add(tuple([product(factors)]))
    for grouping in chain_from_iterable(
        imap(
            partition,
            set(permutations(factors,len(factors)))
            )):
        result=tuple(sorted(product(group) for group in grouping))
        if result in seen:
            continue
        else:
            seen.add(result)
            yield result

if __name__=='__main__':
    for f in factorings([2,2,3,3,5,7]):
        print(f,end=' ')

产量

(3, 420) (9, 140) (28, 45) (14, 90) (2, 630) (3, 3, 140) (3, 15, 28) (3, 14, 30) (2, 3, 210) (5, 9, 28) (9, 10, 14) (2, 9, 70) (2, 14, 45) (2, 7, 90) (3, 3, 5, 28) (3, 3, 10, 14) (2, 3, 3, 70) (2, 3, 14, 15) (2, 3, 7, 30) (2, 5, 9, 14) (2, 7, 9, 10) (2, 2, 7, 45) (2, 3, 3, 5, 14) (2, 3, 3, 7, 10) (2, 2, 3, 7, 15) (2, 2, 5, 7, 9) (2, 2, 3, 3, 5, 7) (5, 252) (10, 126) (18, 70) (6, 210) (2, 5, 126) (5, 14, 18) (5, 6, 42) (7, 10, 18) (6, 10, 21) (2, 10, 63) (3, 6, 70) (2, 5, 7, 18) (2, 5, 6, 21) (2, 2, 5, 63) (3, 5, 6, 14) (2, 3, 5, 42) (3, 6, 7, 10) (2, 3, 10, 21) (2, 3, 5, 6, 7) (2, 2, 3, 5, 21) (4, 315) (20, 63) (2, 2, 315) (4, 5, 63) (4, 9, 35) (3, 4, 105) (7, 9, 20) (3, 20, 21) (2, 2, 9, 35) (2, 2, 3, 105) (4, 5, 7, 9) (3, 4, 5, 21) (3, 3, 4, 35) (3, 3, 7, 20) (2, 2, 3, 3, 35) (3, 3, 4, 5, 7) (7, 180) (3, 7, 60) (2, 18, 35) (2, 6, 105) (3, 10, 42) (2, 3, 6, 35) (15, 84) (12, 105) (3, 5, 84) (5, 12, 21) (7, 12, 15) (4, 15, 21) (2, 15, 42) (3, 5, 7, 12) (3, 4, 7, 15) (2, 6, 7, 15) (2, 2, 15, 21) (21, 60) (30, 42) (6, 7, 30) (5, 7, 36) (2, 21, 30) (5, 6, 6, 7) (3, 12, 35) (6, 14, 15) (4, 7, 45) (35, 36) (6, 6, 35)
于 2011-03-06T00:27:57.603 回答