ユークリッドの最大の共通分母アルゴリズムの時間の複雑さを判断するのが困難です。この擬似コードのアルゴリズムは次のとおりです。
function gcd(a, b)
while b ≠ 0
t := b
b := a mod b
a := t
return a
aとbに依存しているようです。私の考えでは、時間の複雑さはO(a%b)です。あれは正しいですか?それを書くより良い方法はありますか?
ユークリッドの最大の共通分母アルゴリズムの時間の複雑さを判断するのが困難です。この擬似コードのアルゴリズムは次のとおりです。
function gcd(a, b)
while b ≠ 0
t := b
b := a mod b
a := t
return a
aとbに依存しているようです。私の考えでは、時間の複雑さはO(a%b)です。あれは正しいですか?それを書くより良い方法はありますか?
回答:
ユークリッドのアルゴリズムの時間の複雑さを分析するための1つのトリックは、2つの反復で何が起こるかを追跡することです。
a', b' := a % b, b % (a % b)
これで、aとbは1つだけではなく、両方とも減少し、分析が容易になります。あなたはそれをケースに分けることができます:
2a <= b
2b <= a
2a > b
しかしa < b
2b > a
しかしb < a
a == b
次に、すべてのケースで合計a+b
が少なくとも4分の1 減少することを示します。
b % (a % b) < a
と2a <= b
なのでb
、少なくとも半分は減少しているため、少なくとも半分はa+b
減少しています25%
a % b < b
と2b <= a
、のでa
、少なくとも半分a+b
減少し、少なくとも減少25%
b
はb-a
、未満、少なくともb/2
減少a+b
し25%
ます。a
はa-b
、未満、少なくともa/2
減少a+b
し25%
ます。a+b
低下します0
。これは明らかa+b
に少なくとも減少してい25%
ます。したがって、ケース分析では、すべての2ステップがa+b
少なくとも減少し25%
ます。これa+b
が強制的に下回る前に、これが発生する可能性のある最大回数があります1
。S
0に到達するまでのステップ()の総数は、満たす必要があり(4/3)^S <= A+B
ます。今ちょうどそれを働かせます:
(4/3)^S <= A+B
S <= lg[4/3](A+B)
S is O(lg[4/3](A+B))
S is O(lg(A+B))
S is O(lg(A*B)) //because A*B asymptotically greater than A+B
S is O(lg(A)+lg(B))
//Input size N is lg(A) + lg(B)
S is O(N)
したがって、反復回数は入力桁数に比例します。CPUレジスターに収まる数値の場合、一定の時間をとるように反復をモデル化し、gcdの合計実行時間は線形であるように見せかけるのが妥当です。
もちろん、大きな整数を扱う場合は、各反復内のモジュラス演算には一定のコストがないという事実を考慮する必要があります。大まかに言えば、漸近実行時間の合計は、多対数係数のn ^ 2倍になります。のようなもの n^2 lg(n) 2^O(log* n)
。代わりにバイナリgcdを使用することで、多重対数係数を回避できます。
x % y
以上のことはできませんx
未満でなければなりませんy
。a % b
せいぜいそうであり、せいぜい下にあるため、全体としてはより少ないものをa
強制b % (a%b)
します。a
a
アルゴリズムを分析する適切な方法は、その最悪のシナリオを決定することです。ユークリッドGCDの最悪のケースは、フィボナッチペアが関係しているときに発生します。
void EGCD(fib[i], fib[i - 1])
、ここでi> 0。
たとえば、被除数が55で除数が34の場合を選択してみましょう(まだフィボナッチ数を扱っていることを思い出してください)。
お気づきかもしれませんが、この操作には8回の反復(または再帰呼び出し)が必要です。
より大きなフィボナッチ数、つまり121393と75025を試してみましょう。ここでも、24回の反復(または再帰呼び出し)がかかったことがわかります。
また、各反復でフィボナッチ数が得られることにも注意してください。それが、私たちが非常に多くの業務を行っている理由です。実際、フィボナッチ数だけでは同様の結果を得ることができません。
したがって、時間の複雑さは、今回は小さいOh(上限)で表されます。下限は直感的にOmega(1)です。たとえば、500を2で割った場合です。
再帰関係を解決しましょう:
その場合、ユークリッドGCDは最大で log(xy)演算を実行できると言えます。
ウィキペディアの記事でこれをよく見てください。
値のペアの複雑さのすてきなプロットさえあります。
そうではありませんO(a%b)
。
小さい数字の桁数の5倍を超えるステップは決してとらないことがわかっています(記事を参照)。したがって、最大ステップ数は、桁数が増えるにつれて大きくなります(ln b)
。各ステップのコストも桁数が増えるにつれて増加するため、複雑さはO(ln^2 b)
bが小さい数によって制限されます。これは上限であり、実際の時間は通常はそれより短くなります。
n
表していますか?
n = ln b
。big intのモジュラスの通常の複雑さは何ですか?O(log n log ^ 2 log n)
ユークリッドのアルゴリズムの実行時の複雑さを直感的に理解できます。正式な証明は、Introduction to AlgorithmsやTAOCP Vol 2などのさまざまなテキストでカバーされています。
最初に、2つのフィボナッチ数F(k + 1)とF(k)のgcdを取得しようとした場合について考えます。ユークリッドのアルゴリズムがF(k)とF(k-1)に反復することをすぐに観察できます。つまり、反復ごとに、フィボナッチシリーズの数値を1つ下に移動します。フィボナッチ数はO(Phi ^ k)であり、Phiは黄金比です。GCDの実行時間はO(log n)であり、n = max(a、b)であり、logはPhiの底を持っています。次に、これが最悪のケースであることを証明できます。フィボナッチ数が一貫してペアを生成し、残りが各反復で十分な大きさのままであり、シリーズの開始に到達するまでゼロにならないことを確認することで証明できます。
O(log n)を作成することができます。ここで、n = max(a、b)はより厳密にバインドされています。b> = aと仮定して、O(log b)でboundを書き込みます。まず、GCD(ka、kb)= GCD(a、b)であることを確認します。kの最大値はgcd(a、c)であるため、ランタイムでbをb / gcd(a、b)に置き換えると、O(log b / gcd(a、b))の境界がより厳密になります。
最悪のケースは、nとmの両方が連続するフィボナッチ数である場合に発生します。
gcd(Fn、Fn−1)= gcd(Fn−1、Fn−2)=⋯= gcd(F1、F0)= 1で、n番目のフィボナッチ数は1.618 ^ nで、1.618は黄金比です。
したがって、gcd(n、m)を見つけるには、再帰呼び出しの数はΘ(logn)になります。
これは本の分析です Cにおけるデータ構造とアルゴリズム解析により、マーク・アレンワイス(第二版、2.4.4):
ユークリッドのアルゴリズムは、0に達するまで残りを計算し続けることで機能します。最後のゼロ以外の剰余が答えです。
これがコードです:
unsigned int Gcd(unsigned int M, unsigned int N)
{
unsigned int Rem;
while (N > 0) {
Rem = M % N;
M = N;
N = Rem;
}
Return M;
}
がここにあります これから使用定理です。
M> Nの場合、その後、 Mのmod N <M / 2。
証明:
2つのケースがあります。N <= M / 2の場合、剰余はNより小さいため、この場合の定理は真です。もう1つのケースは、N> M / 2です。しかし、Nは、M-N <M / 2で一度Mに入り、定理を証明します。
したがって、以下の推論を行うことができます。
Variables M N Rem
initial M N M%N
1 iteration N M%N N%(M%N)
2 iterations M%N N%(M%N) (M%N)%(N%(M%N)) < (M%N)/2
したがって、2回の反復後、残りは最大で元の値の半分になります。これは、反復回数が最大で
2logN = O(logN)
ます。M> = Nと仮定して、アルゴリズムはGcd(M、N)を計算することに注意してください(N> Mの場合、ループの最初の反復でスワップされます)。
ガブリエルラメの定理は、log(1 / sqrt(5)*(a + 1/2))-2によってステップ数を制限します。ここで、対数の底は(1 + sqrt(5))/ 2です。これは、アルゴリズムの最悪の場合のシーンリオ用であり、入力が連続するフィバノッチ数である場合に発生します。
少しリベラルな境界は次のとおりです。ログa、ログのベースは(sqrt(2))であり、Koblitzによって暗示されます。
暗号化の目的で、ビットサイズがおよそk = logaによって与えられることを考慮して、通常、アルゴリズムのビット単位の複雑さを考慮します。
ユークリッドアルゴリズムのビット単位の複雑さの詳細な分析を次に示します。
ほとんどの参考文献では、ユークリッドアルゴリズムのビット単位の複雑さはO(loga)^ 3によって与えられますが、O(loga)^ 2というより厳しい境界が存在します。
検討してください。r0 = a、r1 = b、r0 = q1.r1 + r2。。。、ri-1 = qi.ri + ri + 1 、. 。。、rm-2 = qm-1.rm-1 + rm rm-1 = qm.rm
a = r0> = b = r1> r2> r3 ...> rm-1> rm> 0 ..........(1)に注意してください。
そしてrmはaとbの最大公約数です。
コブリッツの本のクレーム(理論と暗号のコース)は、ri + 1 <(ri-1)/ 2 .................( 2)
Koblitzでは、kビットの正の整数をlビットの正の整数で除算するために必要なビット演算の数(k> = lと仮定)は、次のように与えられます:(k-l + 1).l ...... .............(3)
(1)と(2)によって除算の数はO(loga)になるため、(3)によって合計の複雑度はO(loga)^ 3になります。
これは、Koblitzの発言によりO(loga)^ 2に削減される可能性があります。
ki = logri +1を考慮
(1)と(2)で、ki + 1 <= ki for i = 0,1、...、m-2、m-1とki + 2 <=(ki)-1 for i = 0 、1、...、m-2
(3)により、m個の分割の合計コストは次のように制限されます:SUM [(ki-1)-((ki)-1))] * ki for i = 0,1,2、..、m
これを再配置:SUM [(ki-1)-((ki)-1))] * ki <= 4 * k0 ^ 2
したがって、ユークリッドのアルゴリズムのビットごとの複雑さはO(loga)^ 2です。
ただし、反復アルゴリズムの場合、次のようになります。
int iterativeEGCD(long long n, long long m) {
long long a;
int numberOfIterations = 0;
while ( n != 0 ) {
a = m;
m = n;
n = a % n;
numberOfIterations ++;
}
printf("\nIterative GCD iterated %d times.", numberOfIterations);
return m;
}
フィボナッチペアでは、差がないiterativeEGCD()
とiterativeEGCDForWorstCase()
、次のような後者のルックス:
int iterativeEGCDForWorstCase(long long n, long long m) {
long long a;
int numberOfIterations = 0;
while ( n != 0 ) {
a = m;
m = n;
n = a - n;
numberOfIterations ++;
}
printf("\nIterative GCD iterated %d times.", numberOfIterations);
return m;
}
はい、フィボナッチペアn = a % n
とn = a - n
、それはまったく同じです。
また、同じ質問に対する以前の回答では、主な減少要因が存在することもわかっていますfactor = m / (n % m)
。
したがって、ユークリッドGCDの反復バージョンを定義された形式で形成するには、次のように「シミュレータ」として表すことができます。
void iterativeGCDSimulator(long long x, long long y) {
long long i;
double factor = x / (double)(x % y);
int numberOfIterations = 0;
for ( i = x * y ; i >= 1 ; i = i / factor) {
numberOfIterations ++;
}
printf("\nIterative GCD Simulator iterated %d times.", numberOfIterations);
}
Jauhar Ali博士の研究(最後のスライド)に基づくと、上記のループは対数的です。
はい。シミュレーターは最大で反復回数を指示するため、小さなああ。ユークリッドGCDでプローブした場合、フィボナッチ以外のペアの反復回数はフィボナッチよりも少なくなります。
a%b
。最悪の場合には、時にあるa
とb
連続したフィボナッチ数です。