数十億の範囲の数値を因数分解するための適切なアルゴリズムは何でしょうか?


8

私は現在Pythonを学んでおり、私が学んでいることを適用する理由を私に与えるために、プロジェクトオイラーのいくつかの問題にひびが入っています。

私は現在、3番目の数値です。これは、その数値の最高の素因数を決定することです。

私はおそらく2つのアルゴリズムが必要であると推定しました。1つは素数性を決定するアルゴリズムで、もう1つは数の要因を見つけることです。

だから私はウィキの記事を読んでいます。使用するのに最適なアルゴリズムとは何か、それをどのように処理するかを決定すること。

しかし、ハードコアな数学ベースのプログラミングを実行してからしばらく経ち、どこかから始めるのに苦労しています。

私は、Fermatの因数分解法を使用してTrial by Divisionを含めることを検討していましたが、RSAをクラックするために複雑すぎないようにしたいのですが、問題に適した2つのアルゴリズムがあり、そこに私の質問があります。

素数性のテスト/目の前の問題に適した数の因数分解にどのアルゴリズムを使用しますか?

編集する

あなたの回答と洞察をありがとうございました。彼らが最も役に立ちました。アドバイスを通じて、またはオイラーの経験を通じて、役立つものすべてに賛成しました。私が正しいとマークしたものは、正しい方向へのプッシュである出発点として適切な場所を与えてくれたので、単に最も有用でした。もう一度ありがとう=)


このような問題は、並列処理を使用するのが最適です。
NoChance

一般的には正しいかもしれませんが、プロジェクトオイラーの場合、通常は「スマート」アルゴリズムを見つけることがより重要です。それらは、総当たり攻撃を並列化するよりもはるかに高速です。
sebastiangeiger

これは数学的に難しい問題であり、理想的な解決策は見つかりません。
DeadMG

回答:


5

これらの問題に対する私のアプローチは通常、これです。通常、ブルートフォースの素朴なアプローチである、それを解決するための最も単純なアルゴリズムを構築し、それが遅すぎるかどうかを数学的にテスト/図化します。ほとんどの場合、それで十分です。アルゴリズムが十分に効率的になるまで、作業を開始して最適化する明確な出発点がない場合。

プロジェクトオイラーの問題3を解決するための簡単なアルゴリズムを次に示します(C言語ですが、Pythonに変換するのは簡単です)。

#include <stdio.h>
#include <math.h>

int isPrime(int n){
    int i;

    if (n==2)
        return 1;

    if (n%2==0)
        return 0;
    for (i=3;i<sqrt(n);i+=2)
        if (n%i==0)
            return 0;
    return 1;
}

int main(){
    long long int n = 600851475143;
    int i = 3;

    while (i<50000){
        if (isPrime(i))
            if (n%i==0)
                printf("%d\n",i);
        i+=2;
    }
    return 0;
}

1
使うのisPrimeはやり過ぎです。ただ、やってn/=2いる間n%2==0、その後、開始i3としてから、ループはif (n%i==0) n/=i; else i+=2;(まあ、それはいったん停止させることができる十分ですi*i > n)。
herby

1
私のプロジェクト解決オイラーの経験は、このアプローチは以前の問題に対して機能するということですが、より複雑な問題を解決するときはおそらくそれを改善する必要があります。
sebastiangeiger

@セバスチャン、私は問題73を抱えています。そうです、道を進んでいると、ほとんどの素朴なアプローチが機能しなくなります。しかし、ねえ、なぜあなたが必要とする前に物事を複雑にするのですか?
Daniel Scocco

2
@ダニエルズ:私はここで馬鹿げているかもしれません。これはどのように問題を解決しますか?a)50000は非常に恣意的です。b)最高のものだけでなく、すべての主要な要素を出力します。c)それが要因であるかどうかを確認する前に、それが素数であるかどうかを確認することは無駄に思われます。d)2は素数です。
pdr 2011

@ pdr、a)素因数はそれほど速くは上昇しないため、50000で十分だと考えました。そうでなければ、常に100000またはsqrt(n)で繰り返すことができます。b)右、つまり、最後に印刷されたものを確認する必要があるだけです(何が起こっているのかを確認するためにすべて印刷しました)。c)テストの反転の方が効率的であることに同意しますが、この場合は何の違いもありません(つまり、数ミリ秒増加します)d)はい、isPrime()関数の特別なケースを忘れました。
Daniel Scocco

4

おそらく他の多くのオイラー問題で再利用するため、因数分解と素数探索(基本的には同じこと)を行うコードを書く価値は十分にあります。後の質問のためにコードを改善し、おそらくそれが十分に効率的でないことがわかった場合は、非網羅的な素数性テストを調べることができるので、今のところ最も簡単なアプローチは次のようにすることをお勧めします。

  • すべての素数を見つける単純なループを記述します(つまり、各数値について、以前に見つかった素数ごとにその可分性をテストし、それらがすべて失敗した場合は、素数のリストに追加します)。
  • 因数分解しようとしている数を、それぞれの素数で数の平方根まで除算してみます。

4

実際、これは数学とコンピューターサイエンスの活発な研究分野です。ウィキペディアの記事は、適切な概要を示しています。

http://en.wikipedia.org/wiki/Integer_factorization

あなたが好きな/興味深いアルゴリズムを見つけて、それを試してみてください。

おそらくトレードオフが必要になります。「優れた」アルゴリズムのほとんどは、本当に理解するためにかなりの数学の背景を必要とします(完全に理解しなくても実装できます)。

どこから始めればよいかわからない場合は、二次篩をお勧めします。

http://en.wikipedia.org/wiki/Quadratic_sieve

非常に数学の知識は必要ありませんが、パフォーマンスは優れています。


2

RubyでProjectEulerのいくつかの問題を素数で試行除算を使用して解決しました。

素数の生成は、実際の因数分解アルゴリズムよりもはるかに重要であることがわかりました。素朴な素数生成アプローチをふるいに置き換えた直後に、実行時間は妥当な量になりました。


1

非常にシンプルに保つ...

Xの因数を見つける:私は(n)を2から開始し、Xの平方根の整数(フロアではなく、丸め)まで作業します。Xをnで除算するとYが生成され、Yは整数です。nとYは要因です。nの最小値は、Yの最大値になります。

Yの原始性:2からYの平方根までループ(m)し、Y / mが整数かどうかを確認します。そうであれば、Yは素数ではありません。別の要因を見つけるために戻ってください。

mがYの根に当たると、素数になります。見ないで。Yが答えです。

nがXの根に当たる場合、素因数はありません。


0

すでに1つの完全なソリューションがあるため、このHaskellのソリューションを投稿します...

--  Problem is to find the largest prime factor of 600851475143
module Factor (testval, bigfactor) where
  testval = 600851475143

  bf' :: Integer -> Integer -> Integer

  bf' f x | (f == x)         = f
          | ((mod x f) == 0) = bf' f (div x f)
          | True             = bf' (f+1) x

  bigfactor :: Integer -> Integer

  bigfactor x | (x <  1) = error "Parameter less than 1"
              | (x == 1) = 1
              | True     = bf' 2 x

基本的に、素数性をテストする必要はありません。非素因数は小さい素因数の積であるため、見つけた因数を分割して(そして繰り返し因数を確実に処理する)場合、非素因数は決して発生しません。

これをGHCiでロードして実行しました-瞬時に、合計4(はい、4!)のオイラー問題が解決されました。


0

また、Pythonの知識も形にしており、githubリポジトリ(https://github.com/rentes/Euler)で Project Eulerの問題に答え始めました。

問題3については、次の前提条件に基づく簡単なソリューションをプログラムしました。

1)正の整数nを指定すると、2、3、...、mで除算を開始し、mが素因数であることがわかった場合は、リストに追加します。私はすでに発見された素因数の倍数をリストに加えていません。たとえば、4は2の倍数であるため、4はこのリストに追加されません。

2)次に、リストの各素数を乗算して、nと等しいかどうかを確認します。等しい場合、nのすべての素因数が見つかりました。そうでない場合は、すべての素因数がnに等しくなるか、mがnに達するまで、nを次のm数で除算し続けます。

詳細については、https://github.com/rentes/Euler/blob/master/problem3.pyを参照してください。私がプログラミングした内容を理解するのに役立つコメントを追加しました。それは単純な解決策であり、最速の解決策ではないと私は確信していますが、それは機能し、理解するのに十分単純です。

宜しくお願いします

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