【C++】モジュロ p における平方根の求め方(p が 4*i + 3 の形の場合)
問題概要
この問題では、整数 n と素数 p が与えられ、「モジュロ p における平方根」を求めます。ここで、p は 4*i + 3 の形、すなわち p % 4 == 3 を満たす素数である必要があります(i > 1)。
この条件に当てはまる素数には、たとえば 7、11、19、23、31 などがあります。
入出力例
入力 : n = 3, p = 7 出力 : 平方根は存在しません
すべての n に対して平方根が存在するとは限りません。n が「平方剰余」でない場合、答えは存在しないため、その判定も行う必要があります。
解法アプローチ①:全探索(線形探索)
最もシンプルな解法は、ループを使う方法です。2 から (p − 1) までの各値について、その値の2乗を p で割った余りが n と一致するかどうかを順番に確認していきます。
コード例
#include <iostream>
using namespace std;
// モジュロ p 上で n の平方根を線形探索する
void findSquareRootMod(int n, int p) {
n = n % p;
for (int i = 2; i < p; i++) {
if ((i * i) % p == n) {
cout << "モジュロ上の平方根は " << i;
return;
}
}
cout << "平方根は存在しません";
}
int main() {
int p = 11;
int n = 3;
findSquareRootMod(n, p);
return 0;
}
実行結果
モジュロ上の平方根は 5
この方法の計算量は O(p) であり、p が大きくなると非効率になるという欠点があります。
解法アプローチ②:公式を使う効率的な方法
p が 4*i + 3 の形の場合、平方根が存在すれば次の公式で直接求められます。
平方根 = ±n(p+1)/4 (mod p)
この公式が成り立つ背景にはオイラーの規準があります。n が平方剰余であれば n(p−1)/2 ≡ 1 (mod p) が成立するため、x = n(p+1)/4 とおくと x2 = n(p+1)/2 = n・n(p−1)/2 ≡ n (mod p) となり、x が確かに平方根になります。
べき乗の計算には繰り返し二乗法(バイナリ累乗)を用いることで、全体の計算量を O(log p) まで抑えることができます。
コード例
#include <iostream>
using namespace std;
// 繰り返し二乗法によるべき乗計算(mod p)
int calcPowerVal(int x, int y, int p) {
int res = 1;
x = x % p;
while (y > 0) {
if (y & 1)
res = (res * x) % p;
y /= 2;
x = (x * x) % p;
}
return res;
}
void squareRoot(int n, int p) {
// p が 4*i + 3 の形かどうかを確認
if (p % 4 != 3) {
cout << "無効な入力です";
return;
}
n = n % p;
int sr = calcPowerVal(n, (p + 1) / 4, p);
if ((sr * sr) % p == n) {
cout << "モジュロ上の平方根は " << sr;
return;
}
// 負の側の平方根(p - sr)も確認
sr = p - sr;
if ((sr * sr) % p == n) {
cout << "平方根は " << sr;
return;
}
cout << "平方根は存在しません";
}
int main() {
int p = 11;
int n = 4;
squareRoot(n, p);
return 0;
}
実行結果
モジュロ上の平方根は 9
まとめ
p が 4*i + 3 の形の素数である場合、モジュロ p 上の平方根は次の2つの方法で求められます。
- 全探索: 2〜(p−1) の各値の2乗を順に確認するシンプルな方法。計算量は O(p)。
- 公式ベース: n(p+1)/4 を繰り返し二乗法で計算する高速な方法。計算量は O(log p)。
競技プログラミングや暗号処理の現場では p が非常に大きな値になるため、実用性を重視するなら公式を使った O(log p) のアプローチを選ぶのがおすすめです。
-
C++で汚染された二分木を復元して要素を検索する方法
問題の概要次のようなルールに従う二分木を考えます。root.val == 0 であるtreeNode.val が x であり、treeNode.left が NULL でない場合、treeNode.left.val = 2 * x + 1 となるtreeNode.val が x であり、treeNode.right が NULL でない場合、treeNode.right.val = 2 * x + 2 となるここで、この二分木は「汚染」されているものとします。つまり、すべてのノードの値が -1 に書き換えられている状態です。まず二分木を復元した上で、以下の FindElements クラスを実
-
C++で数値の立方根を求める方法:二分探索を使った実装を解説
はじめにこの記事では、数値の立方根(3乗根)を求める方法を解説します。例えば、27という数値の立方根は3です。ここでは、標準ライブラリの関数に頼らず、独自のロジックでこの問題を解決します。具体的には「二分探索(バイナリサーチ)」の手法を活用します。アルゴリズムの考え方まず、許容誤差(しきい値)として threshold = 0.000001 を設定します。その上で、以下の手順に従って処理を進めます。左端の値(left)を 0、右端の値(right)を対象の数値として初期化する中央値を mid = (left + right) / 2 として計算する|数値 − mid³| が threshold