10

私は再びプロジェクトオイラーの課題を楽しんでいますが、12番のソリューションは、~593.275 msランスルーごとに最も遅いソリューションの1つであることに気づきました。~1254.593 msこれは、ランスルーごとに10番の私のソリューションに次ぐものです。3 ms私の他のすべての答えは、最もよく下で実行するよりも少ないです1 ms

問題12の私のJavaソリューション:

主要():

int index = 1;
long currTriangleNum = 1;

while (numDivisors(currTriangleNum) <= 500) {
    index++;
    currTriangleNum += index;
}

System.out.println(currTriangleNum);

numDivisors():

public static int numDivisors(long num) {  
    int numTotal = 0;

    if (num > 1)
        if (num % 2 == 0) {
            for (long i = 1; i * i <= num; i++)
                if (num % i == 0)
                    numTotal+=2;
        } else {
            // halves the time for odd numbers
            for (long i = 1; i * i <= num; i+=2)
                if (num % i == 0)
                    numTotal+=2;
    }
    else if (num == 0)
        return 0;
    else if (num == 1)
        return 1;
    else (num < 0)
        return numDivisors(num *= -1);

    return numTotal;
 }

ソリューションフォーラムを見回すと、これらの式(n =(p ^ a)(q ^ b)(r ^ c)...&d(n)=(a + 1)(b + 1)( c + 1)...)彼らのために働いたが、私は個人的にそれがこれ以上速くなるかどうかわからない。手作業で速くなるかもしれませんが、プログラムではそうではありません。

基本的な思考プロセスは次のとおりです。

48 = (2^4)(3^1)48の約数を計算したいと思います。以下の因子ツリーを見ると、 [n =(p ^ a)(q ^ b)(r ^ c)...]と結論付けることができます。

  48
 /  \
2   24
   /  \
  2   12
     /  \
    2   06
       /  \
      2    3 

これを知って、式d(48) = (4+1)(1+1)[d(n)=(a + 1)(b + 1)(c + 1)...]を作成し、48に10個の因子があることを決定します。

d(n)  = (a+1)(b+1)(c+1)...
d(48) = (4+1)(1+1)
d(48) = (5)(2)
d(48) = 10

コードを最適化するにはどうすればよいですか?これらの式は最良の解決策ですか?すべての素因数を見つけて、数式を実装するのは、私がすでに実施しているプログラムよりも時間がかかると思います。

どうもありがとう、

ユスティニアヌス

編集:

誰かがリンクを投稿し始める前に、私は運がなくてもSOで同様の質問を見回しました。私は、すでに配置されているものよりも速く実行されるメソッドの実装を考えることができません。

EDIT2:

エラトステネスのふるいでの2回目の試み(問題10の場合):

int p = 3, n = 2000000;
long total = 0;
boolean[] sieve = new boolean[n];

for (int i = 3; i < n; i += 2)
    sieve[i] = true;

sieve[2] = true;

while (p * p < n) {
    for (int i = p; i < n; i++)
        if (sieve[i] && (i % p) == 0)
            sieve[i] = false;
    p++;

    while (!sieve[p])
        p++;
}

for (int i = 0; i < n; i++)
    if (sieve[i])
        total += i;

System.out.println(total);

で実行され~985.399 msます-他の方法よりもそれほど速くはありませんが、まだ最適化されていません。ただし、機能します。

4

3 に答える 3

10

基礎となる数学的構造を使用すると、プログラムの実行時間が劇的に変わります。ちなみに、これは問題 10 にも当てはまります。数ミリ秒で実行できない場合は、非常に非効率的なアルゴリズムを使用しています。実際、問題 12 はその上に構築されているため、最初に問題 10 に取り組むことをお勧めします。

問題 12 のより良いアルゴリズムを以下に示しますが、最初に、プログラムを大幅に高速化するはずの観察事項を示します。2 つの数値 x と y が互いに素である (つまり、1 以外の公約数がない) 場合、d(x·y) = d(x)·d(y) です。特に、三角形の数の場合、d(n・(n+1)) = d(n)・d(n+1)です。したがって、三角形の数 n·(n+1) を反復する代わりに、n を反復します。これにより、d(n) に渡される引数のサイズが大幅に縮小されます。

この最適化を行うと、d(n) を連続して 2 回 (d((n-1)+1) として 1 回、d(n) として 1 回) 計算することに気付くでしょう。これは、d の結果をキャッシュするのが良い考えであることを示唆しています。以下のアルゴリズムはそれを行いますが、乗算は因数分解よりもはるかに高速であるため、トップダウンではなくボトムアップで d を計算します。


問題 10は、エラトステネスのふるいを適用するだけで解決できます。サイズ 2000000 のブール値 (つまり、ビット ベクトル) の配列を埋めて、sieve[i]==trueifiが素数になるようにします。の数を合計しますsieve[i]==true

問題 12は、エラトステネスのふるいの一般化によって解決できます。が素数sieve[i]かどうかを示すブール値を作成する代わりに、が素数ではない方法の数、つまり の約数を示す数にします。それを行うためにエラトステネスの基本ふるいを変更するのは簡単です: に設定するのではなく、それに 1 を追加します。iisieve[x*y]false

その後のいくつかのプロジェクトのオイラー問題は、同様のアプローチから恩恵を受けています。

問題 12 では、ふるいの計算をいつ停止するかが明確でないという問題が発生する可能性があります。これには 2 つの方法があります:
1. 必要に応じてチャンクごとにふるいを計算します。これは、それ自体が価値のあるプログラミング演習です (これには、2 番目の方法よりも複雑なコードが必要になります)
2. または、境界を過大評価することから始めます: 三角形の数を見つけます。 500 を超える約数がある場合、その数の前またはその数で停止することがわかっています。

n が奇数の場合は d(2^k·n) = (k+1)·d(n) であるため、奇数のみを気にする必要があることに気付き、与えられた k と n のみを見つけると、より多くの時間を節約できます。 (2^k·n) は、バイナリ コンピューターでは高速です。その最適化の詳細は演習として残しておきます。

于 2010-08-01T16:26:08.320 に答える
0

素因数に割り込んで、素因数を追跡して、それらを再計算する必要がないようにすることを検討しましたか?

于 2010-08-01T15:52:39.063 に答える
0

I did this one a while ago, so I don't remember all of the optimizations, here are some:

  1. use summation formula for sum(1...n)
  2. find prime factors with methods described in problem 7 and problem 10
  3. determine how many divisors n has based on its prime factorization*

*Treat this as a probability question if you don't know the "rule". E.g., you have four flavour-shots you can add to your coffee, how many options do you have?

于 2010-08-02T01:27:05.707 に答える