合同式 \(x^2 \equiv a \pmod n\) を満たす整数 \(x\) が存在するとき、\(a\) は \(n\) を法とする「平方剰余」といいます。整数 \(x\) が存在しない場合、\(a\) は \(n\) を法とする「平方非剰余」といいます。簡単な例を示しましょう。次の表を見てください。
mod n x : x*x : 2 3 4 5 6 7 8 9 10 11 ---+-----+------------------------------- 0 : 0 : 0 0 0 0 0 0 0 0 0 0 1 : 1 : 1 1 1 1 1 1 1 1 1 1 2 : 4 : 1 0 4 4 4 4 4 4 4 3 : 9 : 1 4 3 2 1 0 9 9 4 : 16 : 1 4 2 0 7 6 5 5 : 25 : 1 4 1 7 5 3 6 : 36 : 1 4 0 6 3 7 : 49 : 1 4 9 5 8 : 64 : 1 4 9 9 : 81 : 1 4 10 : 100 : 1 : 平方剰余 : 平方非剰余 -------+------------------+--------------- mod 2 : 0, 1 : None mod 3 : 0, 1 : 2 mod 4 : 0, 1 : 2, 3 mod 5 : 0, 1, 4 : 2, 3 mod 6 : 0, 1, 3, 4 : 2, 5 mod 7 : 0, 1, 2, 4 : 3, 5, 6 mod 8 : 0, 1, 4 : 2, 3, 5, 6, 7 mod 9 : 0, 1, 4, 7 : 2, 3, 5, 6, 8 mod 10 : 0, 1, 4, 5, 6, 9 : 2, 3, 7, 8 mod 11 : 0, 1, 3, 4, 5, 9 : 2, 6, 7, 8, 10
上の表では x * x % n を計算しています。0 から x 未満の数で、式 x * x % n で現れる数が平方剰余、現れない数が平方非剰余となります。たとえば、mod 2 の世界では、数は 0 と 1 しかありませんが、0 * 0 % 2 = 0, 1 * 1 % 2 = 1 となるので、0, 1 は平方剰余、平方非剰余の数はありません。
同様に計算していくと、たとえば mod 7 の世界では、0, 1, 2, 4 が平方剰余、3, 5, 6 が平方非剰余となります。つまり、平方数を 7 で割り算した場合、余りが 3, 5, 6 になることはない、ということです。平方数を 3 で割り算すると余りが 0, 1 だけで 2 になることはない、平方数を 4 で割り算すると余りは 0, 1 だけで 2, 3 になることはない、などはよく知られている性質です。
同様に、\(4k, \ 4k + 1, \ 4k + 2, \ 4k + 3\) の二乗を計算すると、0, 1 が平方剰余で 2, 3 が平方非剰余になることが分かります。
数学の整数論では、\(a\) が \(n\) の平方剰余か否かを表す「ルジャンドル記号」が定義されています。
ルジャンドル記号は (a | n) や (a / n) と表記されることがあります。また、\(a \equiv 0 \pmod n\) ならば (a / n) = 0 とする定義もあります。
平方剰余を判定する定理に「オイラーの基準」があります。
簡単な例を示しましょう。
>>> for x in [3,5,7,11,13]: ... for a in range(1, x): ... print(a ** ((x - 1) // 2) % x, end=" ") ... print() ... 1 2 1 4 4 1 1 1 6 1 6 6 1 10 1 1 1 10 10 10 1 10 1 12 1 1 12 12 12 12 1 1 12 1
このように、オイラーの基準で平方剰余を判定することができます。
SymPy にはルジャンドル記号を求める関数 legendre_symbol(a, n) が用意されています。簡単な実行例を示します。
>>> for a in range(1, 20): print(sy.legendre_symbol(a, 3), end=" ") ... 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 >>> for a in range(1, 20): print(sy.legendre_symbol(a, 5), end=" ") ... 1 -1 -1 1 0 1 -1 -1 1 0 1 -1 -1 1 0 1 -1 -1 1 >>> for a in range(1, 20): print(sy.legendre_symbol(a, 7), end=" ") ... 1 1 -1 1 -1 -1 0 1 1 -1 1 -1 -1 0 1 1 -1 1 -1 >>> for a in range(1, 20): print(sy.legendre_symbol(a, 11), end=" ") ... 1 -1 1 1 1 -1 -1 -1 1 -1 0 1 -1 1 1 1 -1 -1 -1 >>> for a in range(1, 20): print(sy.legendre_symbol(a, 13), end=" ") ... 1 -1 1 1 -1 -1 -1 -1 1 1 -1 1 0 1 -1 1 1 -1 -1
平方剰余 \(x^2 \equiv a \pmod p\) の解の有無を判定するとき、「平方剰余の相互法則」を使うと簡単に判定できることがあります。
>>> - legendre_symbol(4, 7) -1 >>> legendre_symbol(7, 11) -1
>>> legendre_symbol(6, 7) -1 >>> legendre_symbol(7, 13) -1
>>> legendre_symbol(4, 5) 1 >>> legendre_symbol(12, 13) 1 >>> legendre_symbol(16, 17) 1 >>> legendre_symbol(28, 29) 1
>>> legendre_symbol(2, 3) -1 >>> (-1)**((3**2 - 1) // 8) -1 >>> legendre_symbol(2, 5) -1 >>> (-1)**((5**2 - 1) // 8) -1 >>> legendre_symbol(2, 7) 1 >>> (-1)**((7**2 - 1) // 8) 1 >>> legendre_symbol(2, 11) -1 >>> (-1)**((11**2 - 1) // 8) -1
(4 / 7) = (2 / 7) * (2 / 7) = 1 * 1 = 1
>>> legendre_symbol(4, 7) 1
(6 / 11) = (2 / 11) * (3 / 11) = (-1) * (3 / 11)
>>> - legendre_symbol(3, 11) -1 >>> legendre_symbol(6, 11) -1
(15 / 11) = (3 / 11) * (5 / 11) = (5 / 11)
>>> legendre_symbol(5, 11) 1 >>> legendre_symbol(15, 11) 1
合同式 \(x^2 \equiv a \pmod n\) の解 \(x\) は、\(n\) が \(4k + 3 \ (k = 0, 1, 2, \cdots)\) 型の奇素数ならば、次の公式で求めることができます。
上の公式はオイラーの基準から簡単に導出することができます。
それでは実際に試してみましょう。
>>> pow(1, (3 + 1) // 4, 3) 1 >>> 1 * 1 % 3 1 >>> pow(1, (7 + 1) // 4, 7) 1 >>> 1 * 1 % 7 1 >>> pow(2, (7 + 1) // 4, 7) 4 >>> 4 * 4 % 7 2 >>> pow(4, (7 + 1) // 4, 7) 2 >>> 2 * 2 % 7 4 >>> pow(1, (11 + 1) // 4, 11) 1 >>> 1 * 1 % 11 1 >>> pow(3, (11 + 1) // 4, 11) 5 >>> 5 * 5 % 11 3 >>> pow(4, (11 + 1) // 4, 11) 9 >>> 9 * 9 % 11 4 >>> pow(5, (11 + 1) // 4, 11) 4 >>> 4 * 4 % 11 5 >>> pow(9, (11 + 1) // 4, 11) 3 >>> 3 * 3 % 11 9
このように \(4k + 3\) 型の素数であれば、公式を使って簡単に解を求めることができます。
\(4k + 1\) 型の素数の場合、mod 8 で \(8l + 1\) と \(8l + 5\) に分けると、前者は Tonelli–Shanks のアルゴリズムで、後者は公式で求めることができます。アルゴリズムの概要を 参考 URL 7 より引用します。
これらの公式やアルゴリズムについて、M.Hiroi はほとんど理解しておりません (ごめんなさい)。詳しい説明は 参考 URL 7, 8, 13 をお読みくださいませ。有益な情報を公開されている作者様に感謝いたします。
SymPy には合同式 \(x^2 \equiv a \pmod n\) を解く関数 sqrt_mod(a, n) が用意されています。それでは実際に試してみましょう。
>>> for a in range(1, 11): print((a, sy.sqrt_mod(a, 11))) ... (1, 1) (2, None) (3, 5) (4, 2) (5, 4) (6, None) (7, None) (8, None) (9, 3) (10, None) >>> for a in range(1, 13): print((a, sy.sqrt_mod(a, 13))) ... (1, 1) (2, None) (3, 4) (4, 2) (5, None) (6, None) (7, None) (8, None) (9, 3) (10, 6) (11, None) (12, 5) >>> for a in range(1, 17): print((a, sy.sqrt_mod(a, 17))) ... (1, 1) (2, 6) (3, None) (4, 2) (5, None) (6, None) (7, None) (8, 5) (9, 3) (10, None) (11, None) (12, None) (13, 8) (14, None) (15, 7) (16, 4)
ルジャンドル記号 (a / n) の n は奇素数でなければいけませんが、それを奇数に拡張したものが「ヤコビ記号 (jacobi symbol)」です。ヤコビ記号はルジャンドル記号と同じ記号 (a / n) で表します。ヤコビ記号の定義を示します。
上式の左辺がヤコビ記号、右辺がルジャンドル記号です。n が奇素数の場合、ヤコビ記号はルジャンドル記号と同じになります。ヤコビ記号 (a / n) が -1 の場合、ルジャンドル記号と同様に a は平方非剰余ですが、(a / n) が 1 になったからといって、a が平方剰余になるとは限りません。たとえば、n = 15 の場合を考えてみましょう。
>>> for a in range(1, 15): print(a, a*a % 15) ... 1 1 2 4 3 9 4 1 5 10 6 6 7 4 8 4 9 6 10 10 11 1 12 9 13 4 14 1
n = 15 の場合、平方剰余は 1, 4, 6, 9, 10 で、それ以外は平方非剰余になります。たとえば、(2 / 15) を考えてみましょう。ヤコビ記号の定義によると、ルジャンドル記号 (2 / 3) * (2 / 5) の値がヤコブ記号 (2 / 15) の値になります。実際に計算すると次のようになります。
このように、ヤコブ記号 (2 / 15) の値は 1 になりますが、2 は平方剰余ではありません。ヤコブ記号 (a / n) が 1 になるからといって、a が平方剰余であるとは確定できないことに注意してください。
ヤコビ記号はルジャンドル記号と同様の性質や相互法則があります。
これらの性質を使うと、素因数分解しなくてもヤコビ記号を計算することができます。次の例を見てください。
(1001 / 12345) = 1 * (12345 / 1001) [相互法則] = (333 / 1001) [(a / n) = ((a % n) / n] = 1 * (1001 / 333) [相互法則] = (2 / 333) [(a / n) = ((a % n) / n] = (2 / 333)*(1 / 333) [(a*b / n) = (a / n)*(b / n) = (-1) * (1 / 333) [第 2 補充法則] = -1
ヤコビ記号 (1001 / 12345) を求めます。便宜上、(a / n) の a を分子、n を分母と呼ぶことにします。分母 n は奇数でなので、分子 a が奇数であれば、相互法則を適用することができます。1001 % 4 == 1 or 12345 % 4 == 1 より 1 * (123345 / 1001) になるので、分子 % 分母 を計算して、ヤコビ記号は (333 / 1001) になります。このように、分子が奇数であれば相互法則と (a / n) = ((a % n) / n) を適用することで、分子と分母の値を小さくしていくことができます。
同様に、(333 / 1001) は (2 / 333) になります。分子が偶数の場合、((a * b) / n) = (a / n) * (b / n) を適用して、式を (2 / n) *((a / 2) / n) に変形し、そこに第 2 補充法則を適用します。333 % 8 == 5 より、(2 / 333) * (1 / 333) = (-1) * (1 / 333) になります。(1 / 333) の値は 1 なので、ヤコビ記号 (1001 / 12345) の値は -1 となります。
SymPy にはヤコビ記号を求める関数 jacobi_symbol(a, n) が用意されています。それでは実際に試してみましょう。
>>> for a in range(1, 3): print(sy.jacobi_symbol(a, 3), end=" ") ... 1 -1 >>> for a in range(1, 5): print(sy.jacobi_symbol(a, 5), end=" ") ... 1 -1 -1 1 >>> for a in range(1, 7): print(sy.jacobi_symbol(a, 7), end=" ") ... 1 1 -1 1 -1 -1 >>> for a in range(1, 9): print(sy.jacobi_symbol(a, 9), end=" ") ... 1 1 0 1 1 0 1 1 >>> for a in range(1, 11): print(sy.jacobi_symbol(a, 11), end=" ") ... 1 -1 1 1 1 -1 -1 -1 1 -1 >>> for a in range(1, 13): print(sy.jacobi_symbol(a, 13), end=" ") ... 1 -1 1 1 -1 -1 -1 -1 1 1 -1 1 >>> for a in range(1, 15): print(sy.jacobi_symbol(a, 15), end=" ") ... 1 1 0 1 0 0 -1 1 0 0 -1 0 -1 -1
n が奇素数の場合、jacobi_symbol() の値はルジャンドル記号と同じ値になっていますね。n が奇素数ではない場合、gcd(a, n) != 1 のとき jacobi_symbol() の値は 0 になります。