Pythonでロシア農民乗算法を使って複素数のべき乗を高速に計算する方法
この記事では、「ロシア農民乗算法(Russian Peasant Multiplication)」の考え方を応用して、複素数のべき乗を効率よく計算するPythonプログラムを解説します。
問題設定は次のとおりです。4つの整数 p、q、r、k が与えられたとき、複素数 (p + qi) の r 乗を求めます。結果は a + bi の形で表せるので、a mod k と b mod k のペアを答えとして返します。
たとえば p = 3、q = 0、r = 8、k = 10000 が入力された場合を考えてみましょう。q = 0 なので計算は単純な 3 の 8 乗、つまり 6561 になります。したがって出力は (6561, 0) です。
アルゴリズムのポイント
ロシア農民乗算法は、指数を半分ずつ減らしながら計算を進める手法で、いわゆる「繰り返し二乗法(バイナリ法)」と同じ発想です。複素数の場合も、次の性質を利用すれば同様に高速化できます。
(p + qi)2 = (p2 − q2) + (2pq)i
この式のおかげで、指数 r が偶数なら底を2乗して指数を半分にでき、奇数なら1回だけ掛けて偶数に帰着させられます。その結果、単純に r 回掛け算を繰り返す O(r) の方法に対し、O(log r) で計算が完了します。
解法の手順
- r = 0 のとき: 1 を返します。
- r = 1 のとき: (p mod k, q mod k) を返します。
- r が偶数のとき: 実部を (p*p − q*q) mod k、虚部を 2*p*q mod k として、solve(p, q, r/2, k) を再帰的に呼び出します。
- r が奇数のとき: まず solve(p, q, r−1, k) の結果を (pr, qr) として受け取り、((p*pr − q*qr) mod k, (p*qr + q*pr) mod k) を返します。これは複素数の掛け算 (p + qi)(pr + qr・i) を展開した形に相当します。
Pythonでの実装例
それでは、実際のコードを見てみましょう。
def solve(p, q, r, k):
if r == 0:
return 1
elif r == 1:
return (p % k, q % k)
elif r % 2 == 0:
return solve((p*p - q*q) % k, 2*p*q % k, r//2, k)
else:
(pr, qr) = solve(p, q, r-1, k)
return ((p * pr - q * qr) % k, (p * qr + q * pr) % k)
print(solve(3, 0, 8, 10000))
入力
3, 0, 8, 10000
出力
(6561, 0)
出力の (6561, 0) は、38 = 6561 であり虚部が 0 であることを示しています。期待どおりの結果ですね。
まとめ
ロシア農民乗算法の「指数を半分にしながら進める」という発想は、複素数のべき乗にもそのまま応用できます。また、途中で剰余を取りながら計算することで巨大な数による桁あふれを防げるため、競技プログラミングなどでも役立つ実用的なテクニックです。
-
Pythonで学ぶ選択ソートの基本原理と実装方法をわかりやすく解説
本記事では、選択ソート(Selection Sort)の基本的な仕組みと、Python 3.xでの実装方法について詳しく解説します。 選択ソートとは? 選択ソートは、ソートされていない部分から最小値の要素を繰り返し見つけ出し、それを先頭に移動させることで配列全体を整列していくアルゴリズムです。処理の過程では、与えられた配列が次の2つの部分配列に分けられます。 すでにソートが完了している部分配列 まだソートされていない部分配列 選択ソートの各イテレーション(反復処理)では、未ソート部分から最小要素を取り出し、ソート済み部分の末尾に挿入していきます。この操作を繰り返すことで、最終的に配列全体
-
【Python】配列の全要素の積をnで割った余りを求めるプログラムの書き方
本記事では、以下の問題に対する解決策について詳しく解説します。問題文複数の数値からなる配列と整数 n が与えられたとき、配列内のすべての要素を掛け合わせた結果を n で割った余りを出力する必要があります。アプローチまず、arr[i] % n のように各要素の余りを個別に計算します。次に、その余りを現在の結果に掛け合わせます。掛け算を行うたびに再度剰余演算を適用することで、オーバーフローを回避できます。この手法は、モジュラー算術(合同式)の分配則に基づいています。( a * b) % c = ( ( a % c ) * ( b % c ) ) % c実装例def findremainder(ar