Erathosthenesのふるいを使用した除数の総和関数


7

私が出会った次のような問題オンライン問題バンクから:にそこにあるの和を計算するように要求照会それぞれが の約数の和である。それが与えられている。 105 

k=LRσ(k)
σ(k)k1LR5106

私の解決策(以下で説明)はErathosthenesのふるいに基づいています。私はそれをC ++で実装しましたが、平均で約秒で動作し、遅すぎます。この問題は少なくとも2倍速く解決できることはわかっていますが、その方法はわかりません。0.9

だからここに私の解決策があります(配列は0ベースです):

M = 5 * 1e6
M = array of zeroes of size M + 1
A[1] = 1
for (k = 2; k <= M; k += 1)
    for (j = k; j <= M; j += k)
        A[j] += k

可能な最大値を下回る各について、Erathosthenesのふるいを介してを事前計算します。メインループがに達すると、 は値を保持します。次に、をσ(k)kkA[k]σ(k)A[k]i=1kσ(i)。そのような前処理の後、すべてのクエリはO(1) 計算による時間 A[R]A[L1]

どうすれば速くできますか?私は2つの式を知っています:

(a)     σ(p1a1psas)=i=1spiai+11pi1
(b)     k=1nσ(k)=k=1nknk

(a)の問題は、それを計算すること(少なくとも私の実装では)が上記よりも遅いことです。(b)の問題は、そのようなアプローチでプレフィックスの合計を計算する方法が、O(n2) 時間。

この問題のためのより効率的なアルゴリズムはありますか?

(問題の銀行は、問題の元の原因を2012年のハリコフ、冬の学校、セルゲイコペロビッチの日、問題Hとしています。)


私が正しく理解している場合、大きなLookUp Tableを作成してからクエリに応答し、実行時にすべてを行い、ボトルネックがLookUpを計算していますか?2つあります。ループを再配置して、作業を異なる方法で分割できますか?メモリと時間に制限があり、プログラムサイズには制限がない場合、テーブルの一部をオフラインにできますか?
邪悪な

あなたは正しく理解しています。ループを並べ替える方法はわかりませんが、線形ふるいと式(a)を使用した計算の方が速いと思います。
イゴール

これは「現実の世界」の問題ですか、それとも(たとえば)プログラミングや数学のコンテストでは「不自然な」問題ですか?テーブルを最も効率的に計算することについて本当の質問がありますが、かなり単純な実装でも「中程度」のサイズ全体を計算できます106ほんの数秒以内にテーブルを作成し、その後(descrから明らかに)すべての後続のクエリはO(1)テーブルルックアップです。それで問題は何ですか?とにかく、その工夫された、明らかにそうである場合、それを現実の問題のように聞こえるようにしようとする最初の初期の問題設定が嫌いです。基本的に適用される数論...
vzn

考案された問題については、表に有限の制限を与えず、代わりに再定式化/集中して、さまざまなアプローチのO(f(n))効率について質問することを提案します。これはO(f2(n))時間に改善されますか?」とにかく、さらなる分析のためにComputer Science Chatを試してください
vzn

@vzn私の質問に注目していただきありがとうございます。問題の原因は問題になっていますが、それは「現実の世界」ではありません。それは超高速の科学計算ではなく、シンプルで中程度に効率的なアルゴリズムに関するものです。
Igor

回答:


5

これは実際にはコンピュータサイエンスではありません...

k = 1からMまでのkの約数の合計を格納するテーブルdを作成します。ここでM = 5·106。それが時間的に重要な部分です。次に、すべての1≤j≤kの約数の合計を格納するテーブルsを作成します。k= 1からMまでです。これは簡単です。s0=0sk+1=sk+dk+1。そしてf(L、R)=sRsL1

最初の表は問題です。あなたはこれを処理しますOログ。そして、あなたはファクター2だけが必要だとあなたは言う...

500万のエントリを持つ配列dができます。おそらく、エントリあたり4バイト= 20メガバイトです。家庭用コンピューターに搭載されている一般的なプロセッサーでは、20メガバイトはどのキャッシュにも適合しません。そして、あなたのコードはその配列の要素に多くのランダムな順序でアクセスします。潜在的な約数kごとに、kで割り切れるすべての数値にアクセスし、約数の合計をkずつ増やします。

より少ない訪問でそれをしましょう:kで割り切れるjを訪問するとき、2つの除数kとj / kを追加します。しかし、それを行うときは、j=k2、kのみを追加します(k = j / kであり、除数を2回カウントする必要がないため)。その後、kおよびj / kを追加して、さらにjにします。j / kはk + 1、k + 2、k + 3などに等しいため、除算する必要はありません。k= 1の場合の配列を初期化します。つまり、A [j] = 1 + j /を設定します。 j≥2の場合は1。

A [1] = 1
for (j = 2; j ≤ M; j += 1)
    A [j] = 1 + j

for (k = 2; k*k ≤ M; k += 1)
    j = k*k
    A [j] += k
    j += k
    s = k + (k + 1)
    while j ≤ M
        A [j] += s
        j += k
        s += 1 // s equals k + j / k

操作を保存しません。ただし、配列Aにはるかに規則的なパターンでアクセスしているため、項目へのアクセスが高速になるため、時間を節約できます。jは小さくなり、各jの反復数が大きくなるため、分岐予測がよりうまく機能します。

さらに改善するには、コンピューターのプロセッサキャッシュに適合する配列項目の数を調べ、配列の部分範囲のみに対してコード全体を実行します(たとえば、A [0]からA [99999]のみを変更し、次にAを変更します。 [100000]からA [199999]など)。このように、ほとんどのメモリアクセスはキャッシュメモリのみにアクセスします。

サイズMのテーブルでN回のルックアップを行っています。MがNより大幅に大きい場合は、このテーブルを構築しないアプローチを検討する必要があります。ルックアップごとにかなり遅くなる可能性がありますが、少数のルックアップ。ここでN≤100,000でM = 5,000,000の場合でも、表の約数1、2、3、4、j / 1、j / 2、j / 3、j / 4を数えないことがあります(これにより、ビルドするのが少し速くなります)、そしてルックアップ中にそれを処理します。

または、奇数の除数の合計のみを追加してから、偶数の除数の合計を計算することもできます(奇数kの除数の合計がsの場合、2kの合計は3s、4kの場合は7sになります。 、8kの場合は15秒など)、ほぼ2倍の節約になります。

PS。私はそれを測定しました... jとk / jの両方を追加することにより、除数のすべての合計をカウントするアルゴリズムをよりキャッシュフレンドリーにし、速度を2倍にしました。最初に奇数kの除数の合計を計算し、次に奇数の値から偶数kを計算すると、合計で7倍速くなります。明らかにすべてが一定の要因です。


3

ですから、問題を少し整理しましょう。プライムシーブを使用することは役立つはずですが、通常のエラトホステンシーブは十分ではありません。

必要なのは、線形時間で機能する素数のふるいであり、すべての数値を一度だけヒットします。線形時間プライムシーブの
1つの説明は、すべての数値を一度だけ交差する方法を示しています。 メリットは何ですか?まあ、数値を交差させる代わりに、そこに除数の合計を挿入する場合、除数を配置する高速アルゴリズムがあります(覚えておいてください
1 除数として)。

また、追加のステップが1つあり、素数は計算されないため、1に遭遇すると、この数+ 1として除数を書き込む必要があります。

次に、累積パスがあるはずです(前のすべての除数の合計になるように配列に最後の項目を追加します)。

この方法では、すべての数値が正確に1回だけ書き込まれる必要があるため、これは確かに元の試みよりも優れています。

他に何ができるでしょうか?
クエリは数値より少ないので、配列全体の計算を省略できると思いましたか?

これは少なくとも2つの方法で実行できます。明らかなのは、部分(または全体)配列をオフラインにして(時間測定中ではない)、プログラムを大きくすることですが、サイズの制限はありませんでした。

もう1つは、累積除数の配列全体を計算し、インデックスから結果を取得するいくつかの関数を当てはめることです。

関数自体は少し複雑かもしれませんし、思考を容易にするために、それを範囲に分割することもできます-短くて見つけやすくします。
その背後にある非常に複雑な作業はオフラインで行われ、実行中はふるいがないため、クエリの時間のみが問題になります。


-1

間隔{L = 1、R = k * 10 ^ 4}および約2 * 10 ^ 4の数値のみの総当たりの事前計算結果を保存できます


1
問題は、事前に計算された結果の作成に時間がかかりすぎることです。
gnasher729 2016年

なぜそれが良いアプローチなのでしょうか?
ラファエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.