数日前、就職の面接があり、C 言語を使用して 1 から 10000 までの 2 または 5 の倍数の合計を計算する方法を尋ねられました。これは私がしました:
int mult2_5()
{
int i, sum=0;
for(i = 1; i <= 10000; i++)
if(i % 2 == 0 || i % 5 == 0)
sum += i;
return sum;
}
これよりも高速な実装だったのだろうか?
数日前、就職の面接があり、C 言語を使用して 1 から 10000 までの 2 または 5 の倍数の合計を計算する方法を尋ねられました。これは私がしました:
int mult2_5()
{
int i, sum=0;
for(i = 1; i <= 10000; i++)
if(i % 2 == 0 || i % 5 == 0)
sum += i;
return sum;
}
これよりも高速な実装だったのだろうか?
モジュラス演算子は非効率的です。より高速な実装は次のようになります。
int multiply2_5(int max)
{
int i, x2 = 0,x5 = 0,x10 = 0;
for(i = 2; i < max; i+=2) x2 += i; // Store all multiples of 2 O(max/2)
for(i = 5; i < max; i+=5) x5 += i; // Store all multiples of 3 O(max/5)
for(i = 10; i < max; i+=10) x10 += i; // Store all multiples 10; O(max/10)
return x2+x5-x10;
}
このソリューションでは、10 の倍数を取り出す必要がありました。これは、2 と 5 の倍数が 10 であるため、2 番目のループでは、最初のループで既に追加された 10 の倍数が追加されるためです。3 つのループを組み合わせると、O(8/10 最大) になります。
別のより良い解決策は、数学的アプローチを取る場合です。
この 2 + 4 + 6 + 8 ... 10000 と 5 + 10 + 15 +20 + ... 10000 のようなすべての数値を合計しようとしています。これは 2 * (1 + 2 + 3 + 4 + … + 5000) と 5 * (1 + 2 + 3 + 4 + ... + 2000) の場合、「n」個の自然数の和は (n * (n + 1)) ( source ) なので、次のように一定の時間:
int multiply2_5(int max)
{
// x = 2 + 4 + 6 + ... = 2 * (1 + 2 + 3 +...)
// y = 5 + 10 + 15 + ... = 5 * (1 + 2 + 3 +...)
// The sun of n natural numbers is sn = (n (n + 1)) / 2
int x2 = max/ 2; // 2 * ( 1 +2 + 3 … max/2)
int x5 = max /5; // 5 * ( 1 +2 + 3 … max/5)
int x10 = max/ 10;
int sn2 = 0.5 * (x2 * (x2+1)); // (n * (n + 1)) / 2
int sn5 = 0.5 * (x5 * (x5+1));
int sn10 = 0.5 * (x10 * (x10+1));
return (2*sn2) + (5 *sn5) - (10*sn10);
}
それが「より速い」という意味であれば、最初に紙の上でそれを解決してください。
$2\sum_{1<=2k<=10000}k + 5\sum_{1<=5k<=10000} - 10\sum_{1<=10k<=10000}k$
申し訳ありませんが、私のSO方程式-fuは弱いです...とにかく、このルートは、紙の上でほとんど処理できるものを提供します:いくつかのステップを減らした後、5000 * 6001
int
mult2_5(void)
{
return 5000*6001;
}
プロジェクト オイラーの問題 1は非常によく似ています。これに対する解決策を投稿した人はたくさんいます。
以前の回答で述べたように、関連する倍数を明示的にループする方が、ループごとに残りをテストするよりも優れています。ただし、10 の倍数を計算して減算する必要はありません。5 から始めて 10 ずつ進めるだけで、すべてをスキップできます。
int multiply2_5b(int max)
{
int i, x2 = 0,x5 = 0;
for (i = 2; i < max; i += 2) x2 += i; // Sum all multiples of 2
for (i = 5; i < max; i += 10) x5 += i; // Sum all odd multiples of 5
return x2 + x5;
}
これは、数学を使用して行うことができます。2 * sum(1 to 5000) + 5 * sum(1 to 2000) - 10 * sum(1 to 1000).
練習問題として残されている、off-by-one エラーのようなもの。
35 (2 + 4 + 5 + 6 + 8 + 10 の合計) で始まる単純なループを 60 のステップで実行することにより、純粋で単純な乗算にほぼ到達しました。次のくじを取る 例: 12 + 14 + 15 + 16 + 18 + 20 など for(int i=35;i<5976;i=i+60) { sum=sum+i } 5976 は 5975 が最後であることに由来しますつまり、このループは 100 回実行され、最初のターンで合計が 35 増加し、残りの 99 回で合計が 60 増加することがわかります。これは、単純な乗算などに還元できるようになりました。