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


105

これは非常にばかげた方法です:

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

私が得たい結果はこれに似ていますが、よりスマートなアルゴリズムが必要です(このアルゴリズムは非常に遅くて馬鹿げています:-)

素因数とその多様性を十分に速く見つけることができます。私はこのように因子を生成するジェネレーターを持っています:

(factor1、multiplicity1)
(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

更新:グレッグ・ヒューギルと彼の「スマートな方法」に感謝します:) 100000000のすべての除数を計算すると、私のマシンで馬鹿げた方法が取った39に対して彼の方法で0.01秒かかりました。非常にクールです:D

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


それはこの質問には、「与えられた数の約数の数を計算するアルゴリズム」のほぼ重複したことが示唆されたことを理由は、問題の提案最初のステップはするということだった除数のすべてを見つける私は正確であると信じて、何をしようとしていたの?
Andrew Edgecombe

4
アンドリューは、約数がいくつあるかを見つけるには、素因数を見つけ、それを使って約数がどれだけあるかを数える必要があります。その場合、除数を見つける必要はありません。
ロイック・フォーレ-ラクロワ

1
@Andrea Ambu、関数名を修正してください
ミネラル

回答:


77

あなたの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ます。


2
すごいクールな方法(n / 2で停止)をとった39に対して100000000のすべての約数を計算するのに0.01かかったわ、ありがとう!
アンドレア・アンブ

47
Pythoneseを理解していない私たちにとって、これは実際に何をしているのでしょうか?
Matthew Scharley、2008年

1
一酸化炭素:これは、与えられた因子のすべての乗法の組み合わせを計算します。そのほとんどは一目瞭然です。「yield」行はリターンのようなものですが、値を返した後も継続します。[0] * nfactorsは、長さnfactorsのゼロのリストを作成します。reduce(...)は、因子の積を計算します。
グレッグヒューギル

reduceとlambdaの表記は、実際に私を混乱させていた部分でした。再帰関数を使用してC#でこれを行うためのアルゴリズムを実装して因子の配列を
調べ

3
これはもちろん、n / 2またはsqrt(n)までのすべての数で除算するよりも劇的に優れていますが、この特定の実装には2つの欠点があります。しかし、私はPythonがパフォーマンスを殺すことについてであるとは思わない。問題2:除数が順番に返されない。
Tomasz Gandor 2014

34

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]

2
なぜなら、1..10の間の要素のリストを取得すると、11..100の間の任意の要素を簡単に生成できるからです。{1、2、4、5、10}を取得します。これらの各要素とあなたの{100、50、20、25、10}で100を割ります。
Matthew Scharley、2008年

2
ファクターは常に、定義によりペアで生成されます。唯一のsqrt(N)を検索することにより、あなたはパワー2であなたの仕事をカットしている
マシューScharley

それは私の投稿のバージョンよりも非常に高速ですが、素因数を使用したバージョンよりもまだ遅すぎます
Andrea Ambu

これは最善の解決策ではないことに同意します。私は、すでに多くの時間を節約できる「ダム」検索を行う「より良い」方法を単に指摘していました。
Matthew Scharley、2008年

因数分解はNP困難であることが示されていません。 en.wikipedia.org/wiki/Integer_factorization そして、問題は、素因数(難しい部分)がすでに見つかっていることを考えると、すべての除数を見つけることでした。
ジェイミー、

19

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

これは:

  • 読みやすい
  • ショート
  • 自己完結型、コピー&ペースト準備完了
  • 素早い(多くの素因数と除数がある場合、受け入れられているソリューションよりも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

私が代わるwhile i*i <= nnことにより、while i <= limitどこ、limit = math.sqrt(n)
Rafa0809

17

立ち寄れると思います math.sqrt(n)n / 2の代わりに。

わかりやすいように例を挙げます。今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う。

28の約数

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

出典:数値の約数を決定する方法


2
ナイスナイス!!math.sqrt(n) instead of n/2エレガンスのために必須
Rafa0809

これは誤りです。nがそれ自体で割り切れるのを忘れました。
-jasonleonhard

1
素敵な答え。シンプルで明確。しかし、python 3には2つの必要な変更があります。n/ iはint(n / i)を使用して入力する必要があります。これにより、n / iは浮動小数点数を生成します。また、rangexはpython 3で非推奨になり、rangeに置き換えられました。
Geoffroy CALA、2018年

7

私はGregソリューションが好きですが、それがもっとPythonのようになってほしいです。私はそれがより速く、より読みやすくなると思います。なので、しばらくコーディングして、これを思いつきました。

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

「Factorgenerator」は辞書を返すようになりました。次に、ディクショナリは「除数」にフィードされます。「除数」を使用して、最初にリストのリストを生成します。各リストは、p素数のp ^ n形式の因子のリストです。次に、これらのリストのデカルト積を作成し、最後にGregの解を使用して除数を生成します。仕分けして返却します。

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

ピエトロ・スペローニ(pietrosperoni dot it)

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)

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


Python 2.6にはitertools.product()があります。
jfs 2008

どこでもlist.appendの代わりにジェネレータを使用するバージョンは、よりクリーンになる可能性があります。
jfs 2008

エラトステネスのふるいは少ないし、素数を生成するために使用されるか、または等しいSQRT(N)ことができるstackoverflow.com/questions/188425/project-euler-problem#193605
JFS

1
コーディングスタイル:指数= [kの場合はk ** x、factors.items()の場合はv(範囲(v + 1)の場合はx)]
jfs 2008

listexponentsの場合:[[k ** x for x in range(v + 1)] for k、v in Factors.items()]
klenwell

3

これは、純粋なPython 3.6で最大10 ** 16までの数値に対してそれを行うためのスマートで高速な方法です。

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

素数を見つけて因数分解するために使用されるアルゴリズムの名前は何ですか?これをC#で実装したいので..
Kyu96

2

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

1
エラーが発生しているようです:NameError: global name 'prime_factors' is not defined。他の回答も、元の質問も、これが何をするかを定義していません。
AnnanFay

2

私は、将来参照するために、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

古い質問ですが、これが私の見解です:

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

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


0

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

それはパイソンですか?とにかく、それは確かにpython 3.xではありません。
ジンキン2014年

これは疑似コードであり、Pythonに簡単に変換できるはずです。
user448810 14年

3年遅れて、決して遅れることはありません:) IMO、これはこれを行うための最も単純で最短のコードです。比較表はありませんが、i5ポータブルラップトップで最大100万分の1の因子を因数分解して計算できます。
Riyaz Mansoor

0

これが私の解決策です。それは馬鹿げているように見えますが、うまく機能します...そして私はすべての適切な除数を見つけようとしていたので、ループは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

typo:ファクトを返す=> faclistを返す
Jonath P

0

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

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

PCに大量のメモリが搭載されている場合、ブルートシングルラインはnumpyで十分高速です。

N = 10000000; tst = np.arange(1, N); tst[np.mod(N, tst) == 0]
Out: 
array([      1,       2,       4,       5,       8,      10,      16,
            20,      25,      32,      40,      50,      64,      80,
           100,     125,     128,     160,     200,     250,     320,
           400,     500,     625,     640,     800,    1000,    1250,
          1600,    2000,    2500,    3125,    3200,    4000,    5000,
          6250,    8000,   10000,   12500,   15625,   16000,   20000,
         25000,   31250,   40000,   50000,   62500,   78125,   80000,
        100000,  125000,  156250,  200000,  250000,  312500,  400000,
        500000,  625000, 1000000, 1250000, 2000000, 2500000, 5000000])

遅いPCで1秒もかかりません。


0

ジェネレーター関数による私の解決策は:

def divisor(num):
    for x in range(1, num + 1):
        if num % x == 0:
            yield x
    while True:
        yield None

-1
return [x for x in range(n+1) if n/x==int(n/x)]

3
質問者は、よりきれいなフォーマットだけでなく、より良いアルゴリズムを求めました。
Veedrac 2014年

4
ゼロによる除算を回避するには、range(1、n + 1)を使用する必要があります。また、Python 2.7を使用している場合は、最初の除算にfloat(n)を使用する必要があります。ここでは、1/2 = 0
イェンス・ムンク

-1

私にとってこれは問題なく動作し、クリーンでもあります(Python 3)

def divisors(number):
    n = 1
    while(n<number):
        if(number%n==0):
            print(n)
        else:
            pass
        n += 1
    print(number)

それほど高速ではありませんが、必要に応じて1行ずつ除数を返します。本当に必要な場合は、list.append(n)およびlist.append(number)を実行することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.