1

たとえば、1260のように数値係数があるとします。

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

これらの数値から可能なすべてのサブプロダクト、つまり素数分解だけでなく、因子の合計がmax_product未満のすべての因数分解を使用して、Pythonの組み合わせで行うのに最適な方法はどれですか?

素因数から組み合わせを行う場合、組み合わせていない残りの部分がわからないため、製品の残りの部分をリファクタリングする必要があります。

除数関数を改良して、サイズ順に除数の代わりに除数のペアを生成することもできますが、それでも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)

おそらく、除数が数である数にリンクできる、より小さな除数用のふるいまたは動的計画法ソリューションを作成するための何らかの方法が必要になるでしょう。ただし、重複を避けるのは難しいようです。すべての数の完全な因数分解を保存せずに因数分解を高速化するために、各数の最大の素因数を格納する1つのふるい関数を用意しています...おそらくそれを適応させることができます。

更新:因子の合計は積に近いはずなので、おそらく答えには10未満の因子が多数あります(最大14因子)。

UPDATE2: これが私のコードですが、2を超える部分に対して再帰的または反復的に複数の削除を行う方法を理解し、字句分割を掘り下げて、重複を生成するジャンプビットパターンを置き換える必要があります(1回の置換でのみ哀れなヒット数、 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

解決 策私は受け入れられた答えを細かい解決策から削除しませんが、それは仕事にとって非常に複雑です。プロジェクトオイラーから、ニュージーランドのオービフォールドからのこのヘルパー関数のみを明らかにします。これは、最初に素因数を必要とせずに、より高速に動作します。

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]]

私のタイミングデコレータによるPython2.7での4.85秒での彼の実行の問題88に関連する解決策と、サイコを使用した場合は2.6.6で、サイコを使用しない場合は2.7で3.7秒のカウンタを検出して停止条件を最適化した後。私自身のコードの速度は、受け入れられた回答のコード(私が追加したソートを削除)の30秒から、Python 2.6.6の2.25秒(psycoなしの2.7)およびpsycoの782ミリ秒になりました。

4

3 に答える 3

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 に答える
1

[(2, 9), (3, 3)](for )のような[2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3]リストを、拡張されていない要素のベースリストおよび拡張された要素のリストとして使用します。各ラウンドで、ベースから1つの要素を選び、その数を減らし、

  • 展開されたリストに追加して長さを増やし、合計で1つの追加要素があります(cufoffまで)
  • それをすべての拡張された係数で乗算し、すべての可能性を生成します

動的計画法とカットオフ戦略により、これは非常に高速になりました。

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 に答える