web-dev-qa-db-ja.com

数のすべての約数を取得する最良の方法は何ですか?

これは非常に愚かな方法です:

def divisorGenerator(n):
    for i in xrange(1,n/2+1):
        if n%i == 0: yield i
    yield n

私が得たい結果はこれと似ていますが、よりスマートなアルゴリズムが欲しいです(これは非常に遅くて愚かです:-)

素因数とその多重度を十分に速く見つけることができます。私はこの方法でファクターを生成するジェネレーターを持っています:

(因子1、多重度1)
(factor2、multiplicity2)
(factor3、multiplicity3)
等々...

すなわち、の出力

for i in factorGenerator(100):
    print i

は:

(2, 2)
(5, 2)

私がやりたいこと(これは他の問題のためにコーディングしました)にどれだけ役立つかわかりませんが、とにかくもっとスマートな方法を作りたいです

for i in divisorGen(100):
    print i

これを出力:

1
2
4
5
10
20
25
50
100

UPDATE:グレッグ・ヒューギルと彼の「スマートな方法」に感謝します方法は非常にクールな私のマシンにかかった:D

UPDATE 2:これが this postの複製であると言うのを止めます。与えられた数の約数の計算は、すべての約数を計算する必要はありません。ウィキペディアで「除数関数」を探していないと思われる場合は、別の問題です。投稿する前に質問と回答を読んでください。トピックが何であるかわからない場合は、役に立たず、既に与えられた回答を追加しないでください。

89
Andrea Ambu

FactorGenerator関数を指定すると、動作するはずのdivisorGenは次のとおりです。

def divisorGen(n):
    factors = list(factorGenerator(n))
    nfactors = len(factors)
    f = [0] * nfactors
    while True:
        yield reduce(lambda x, y: x*y, [factors[x][0]**f[x] for x in range(nfactors)], 1)
        i = 0
        while True:
            f[i] += 1
            if f[i] <= factors[i][1]:
                break
            f[i] = 0
            i += 1
            if i >= nfactors:
                return

このアルゴリズムの全体的な効率は、factorGeneratorの効率に完全に依存します。

70
Greg Hewgill

Shimiが言ったことを拡張するには、1からnの平方根までのループのみを実行する必要があります。次に、ペアを見つけるには、n / i、これは問題空間全体をカバーします。

また指摘したように、これはNPまたは「困難な」問題です。徹底的な検索、つまりその方法は、保証された答えを得るのと同じくらい優れています。この事実は、暗号化アルゴリズムなどで使用され、それらを保護します。誰かがこの問題を解決した場合、現在の「安全な」通信のすべてではないとしても、ほとんどが安全ではなくなります。

Pythonコード:

import math

def divisorGenerator(n):
    large_divisors = []
    for i in xrange(1, int(math.sqrt(n) + 1)):
        if n % i == 0:
            yield i
            if i*i != n:
                large_divisors.append(n / i)
    for divisor in reversed(large_divisors):
        yield divisor

print list(divisorGenerator(100))

次のようなリストが出力されます:

 [1、2、4、5、10、20、25、50、100] 
33

N/2ではなくmath.sqrt(n)で停止できると思います。

簡単に理解できるように例を挙げます。 sqrt(28)は_5.29_であるため、ceil(5.29)は6になります。したがって、6で停止すると、すべての除数を取得できます。どうやって?

最初にコードを見て、次に画像を見てください:

_import math
def divisors(n):
    divs = [1]
    for i in xrange(2,int(math.sqrt(n))+1):
        if n%i == 0:
            divs.extend([i,n/i])
    divs.extend([n])
    return list(set(divs))
_

さて、下の画像をご覧ください:

すでに除数リストに_1_を追加しており、_i=2_で始めているとしましょう

Divisors of a 28

したがって、商と除数をリストに追加したため、すべての反復の最後に28の除数がすべて入力されます。

ソース: 数値の約数の決定方法

14
Anivarth

これにはすでに多くの解決策がありますが、私は本当にこれを投稿する必要があります:)

これは:

  • 読みやすい
  • ショート
  • 自己完結型、コピー&ペースト準備完了
  • 素早い(多くの素因数と除数がある場合、受け入れられているソリューションよりも10倍高速)
  • python3、python2、pypyに準拠

コード:

def divisors(n):
    # get factors and their counts
    factors = {}
    nn = n
    i = 2
    while i*i <= nn:
        while nn % i == 0:
            factors[i] = factors.get(i, 0) + 1
            nn //= i
        i += 1
    if nn > 1:
        factors[nn] = factors.get(nn, 0) + 1

    primes = list(factors.keys())

    # generates factors from primes[k:] subset
    def generate(k):
        if k == len(primes):
            yield 1
        else:
            rest = generate(k+1)
            prime = primes[k]
            for factor in rest:
                prime_to_i = 1
                # prime_to_i iterates prime**i values, i being all possible exponents
                for _ in range(factors[prime] + 1):
                    yield factor * prime_to_i
                    prime_to_i *= prime

    # in python3, `yield from generate(0)` would also work
    for factor in generate(0):
        yield factor
14
Tomas Kulich

私はGregソリューションが好きですが、もっとpythonのようにしたいと思います。より速く、より読みやすいと感じています。

リストのデカルト積を作成するには、最初の2つの関数が必要です。この問題が発生しても再利用できます。ところで、私はこれを自分でプログラムしなければなりませんでした。この問題の標準的な解決策を知っている人がいたら、私に連絡してください。

「Factorgenerator」は辞書を返すようになりました。そして、辞書は「除数」に送られます。「除数」を使用して、最初にリストのリストを生成します。各リストは、p ^ nとpプライムの形式の因子のリストです。次に、これらのリストのデカルト積を作成し、最終的にGregのソリューションを使用して除数を生成します。それらをソートして返します。

私はそれをテストしましたが、以前のバージョンよりも少し速いようです。私はそれをより大きなプログラムの一部としてテストしたので、それがどれほど高速であるかは本当に言えません。

ピエトロスペローニ(ピエトロスペローニドットイット)

from math import sqrt


##############################################################
### cartesian product of lists ##################################
##############################################################

def appendEs2Sequences(sequences,es):
    result=[]
    if not sequences:
        for e in es:
            result.append([e])
    else:
        for e in es:
            result+=[seq+[e] for seq in sequences]
    return result


def cartesianproduct(lists):
    """
    given a list of lists,
    returns all the possible combinations taking one element from each list
    The list does not have to be of equal length
    """
    return reduce(appendEs2Sequences,lists,[])

##############################################################
### prime factors of a natural ##################################
##############################################################

def primefactors(n):
    '''lists prime factors, from greatest to smallest'''  
    i = 2
    while i<=sqrt(n):
        if n%i==0:
            l = primefactors(n/i)
            l.append(i)
            return l
        i+=1
    return [n]      # n is prime


##############################################################
### factorization of a natural ##################################
##############################################################

def factorGenerator(n):
    p = primefactors(n)
    factors={}
    for p1 in p:
        try:
            factors[p1]+=1
        except KeyError:
            factors[p1]=1
    return factors

def divisors(n):
    factors = factorGenerator(n)
    divisors=[]
    listexponents=[map(lambda x:k**x,range(0,factors[k]+1)) for k in factors.keys()]
    listfactors=cartesianproduct(listexponents)
    for f in listfactors:
        divisors.append(reduce(lambda x, y: x*y, f, 1))
    divisors.sort()
    return divisors



print divisors(60668796879)

追伸私がstackoverflowに投稿するのは初めてです。フィードバックをお待ちしています。

7
Pietro Speroni

CodeReview からの適応、ここにnum=1

from itertools import product
import operator

def prod(ls):
   return reduce(operator.mul, ls, 1)

def powered(factors, powers):
   return prod(f**p for (f,p) in Zip(factors, powers))


def divisors(num) :

   pf = dict(prime_factors(num))
   primes = pf.keys()
   #For each prime, possible exponents
   exponents = [range(i+1) for i in pf.values()]
   return (powered(primes,es) for es in product(*exponents))
2
YvesgereY

以下は、純粋なPython 3.6、

from itertools import compress

def primes(n):
    """ Returns  a list of primes < n for n > 2 """
    sieve = bytearray([True]) * (n//2)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = bytearray((n-i*i-1)//(2*i)+1)
    return [2,*compress(range(3,n,2), sieve[1:])]

def factorization(n):
    """ Returns a list of the prime factorization of n """
    pf = []
    for p in primeslist:
      if p*p > n : break
      count = 0
      while not n % p:
        n //= p
        count += 1
      if count > 0: pf.append((p, count))
    if n > 1: pf.append((n, 1))
    return pf

def divisors(n):
    """ Returns an unsorted list of the divisors of n """
    divs = [1]
    for p, e in factorization(n):
        divs += [x*p**k for k in range(1,e+1) for x in divs]
    return divs

n = 600851475143
primeslist = primes(int(n**0.5)+1) 
print(divisors(n))
1
Bruno Astrolino

古い質問ですが、ここに私の見解があります:

def divs(n, m):
    if m == 1: return [1]
    if n % m == 0: return [m] + divs(n, m - 1)
    return divs(n, m - 1)

次のプロキシを使用できます。

def divisorGenerator(n):
    for x in reversed(divs(n, n)):
        yield x

注:サポートする言語の場合、これは末尾再帰になります。

1
joksnet

将来の参考のために、Anivarthのわずかに改訂されたバージョンを追加します(最もPythonicだと思います)。

from math import sqrt

def divisors(n):
    divs = {1,n}
    for i in range(2,int(sqrt(n))+1):
        if n%i == 0:
            divs.update((i,n//i))
    return divs
1
ppw0

これが私の解決策です。それは馬鹿げているように見えますが、うまく機能しています...そして、私はすべての適切な除数を見つけようとしていたので、ループはi = 2から始まりました。

import math as m 

def findfac(n):
    faclist = [1]
    for i in range(2, int(m.sqrt(n) + 2)):
        if n%i == 0:
            if i not in faclist:
                faclist.append(i)
                if n/i not in faclist:
                    faclist.append(n/i)
    return facts
0
Amber Xue

factors関数がn(たとえば、factors(60)_がリスト[2、2、3、5]を返す)の因子を返すと仮定すると、 nの約数を計算する関数:

function divisors(n)
    divs := [1]
    for fact in factors(n)
        temp := []
        for div in divs
            if fact * div not in divs
                append fact * div to temp
        divs := divs + temp
    return divs
0
user448810

リスト内包表記の使用にのみ関心があり、他に何も重要ではない場合!

from itertools import combinations
from functools import reduce

def get_devisors(n):
    f = [f for f,e in list(factorGenerator(n)) for i in range(e)]
    fc = [x for l in range(len(f)+1) for x in combinations(f, l)]
    devisors = [1 if c==() else reduce((lambda x, y: x * y), c) for c in set(fc)]
    return sorted(devisors)
0
Sadiq