M.Hiroi's Home Page

Memorandum

数学編

M.Hiroi の数学に関する覚え書です
[ Home | Math ]

初等整数論 [3]

●不定方程式と拡張ユークリッドの互除法

変数の個数が式の本数よりも多い方程式を「不定方程式」といいます。一次不定方程式 \(ax + by = c \ (c \ne 0)\) は、\(a, b, c\) が整数で、\(c\) が \(a\) と \(b\) の最大公約数の倍数であるとき、整数解を持つことが証明されています。これを「ベズーの等式」といいます。ベズーの等式は「拡張ユークリッドの互除法」を使って簡単に解くことができます。

拡張といっても原理はユークリッドの互除法とまったく同じです。\(\gcd(a, b)\) を求めるときに一組の解 \((x, y)\) もいっしょに見つける、というものです。ポイントは数を \(x = xa \times a + xb \times b\) の形で表すところです。次の図を見てください。

\(x = xa \times a + xb \times b, \ y = ya \times a + yb \times b\) とおき、
\(x, y\) に対してユークリッドの互除法を適用する (初期値は \(xa = 1, \ xb = 0, \ ya = 0, \ yb = 1 \))

\(x\) と \(y\) の商を \(q\), 余りを \(z\) とすると

\(\begin{eqnarray} z &=& x - q \times y = xa \times a + xb \times b - q \times (ya \times a + yb \times b) \\ &=& (xa - q \times ya) \times a + (xb - q \times yb) \times b \\ &=& za \times a + zb \times b \end{eqnarray}\)

となる
次は \(y\) と \(z\) で同様の処理を繰り返す
具体的には変数の値を

\(\begin{array}{l} x, xa, xb = y, ya, yb \\ y, ya, yb = z, za, za \end{array}\)

のように更新して処理を繰り返す
\(y\) が \(0\) になったとき、\(x\) が \(a\) と \(b\) の最大公約数で \((xa, xb)\) が一つの解となる

具体的な例を示しましょう。\(4321 \times x + 1234 \times y = \gcd(4321, 1234)\) を解いてみます。

  (x, xa, xb)     (y, ya, yb)      q   (z, za, zb)
  ------------------------------------------------------
  (4321, 1, 0)    (1234, 0, 1)     3   (619, 1, -3)
  (1234, 0, 1)    (619, 1, -3)     1   (615, -1, 4)
  (619, 1, -3)    (615, -1, 4)     1   (4, 2, -7)
  (615, -1, 4)    (4, 2, -7)       153 (3, -307, 1075)
  (4, 2, -7)      (3, -307, 1075)  1   (1, 309, -1082)
  (3, -307, 1075) (1, 309, -1082)  3   (0, -1234, 4321)
  (1, 309, -1082) (0, -1234, 4321)

  4321 * 309 + 1234 * (-1082) = 1 になる

一般解は次のように求めることができます。

\(\begin{array}{clclcl} & 4321 \times x & + & 1234 \times y & = & 1 \\ - & 4321 \times 309 & + & 1234 \times (-1082) & = & 1 \\ \hline & 4321 \times (x - 309) & +& 1234 \times (y - (-1082)) &= & 0 \end{array}\)

となるから \(4321 \times (x - 309) = -1234 \times (y + 1082)\) が成り立つ

\(-1234\) は \(x - 309\) の約数で \(4321\) は \(y + 1082\) の約数だから、\(t\) を整数とすると、

\(\begin{array}{ll} x - 309 = -1234 \times t & \therefore \ x = 309 - 1234 \times t \\ y + 1082 = 4321 \times t & \therefore \ y = -1082 + 4321 \times t \end{array}\)

それでは、実際に値を代入して (x, y) の組を求めてみましょう。

  t    x      y   z = 4321 * x + 1234 * y
 -----------------------------------------
 -4  5245 -18366  1
 -3  4011 -14045  1
 -2  2777  -9724  1
 -1  1543  -5403  1
  0   309  -1082  1
  1  -925   3239  1
  2 -2159   7560  1
  3 -3393  11881  1
  4 -4627  16202  1

ところで、Python のライブラリ SymPy には最大公約数を求める関数 gcd(a, b) や、拡張ユークリッドの互除法を行う関数 gcdex(a, b) が用意されいます。簡単な使用例を示します。

>>> import sympy as sy
>>> sy.gcd(4321,1234)
1
>>> sy.gcdex(4321,1234)
(309, -1082, 1)

●中国の剰余定理

中国の剰余定理 (Chinese remainder theorem) は整数の剰余に関する定理です。具体的には、連立合同式に解があることを示す定理です。

SymPy には中国人の剰余定理を計算する関数 crt([m, n, ...], [a, b, ...]) が用意されています。関数 crt() は m で割った余りが a で、n で割った余りが b となる整数を求めます。

簡単な使用例を示します。3 で割ると 2 余り、5 で割ると 4 余る数を求めます。

>>> sy.ntheory.modular.crt([3,5],[2,4])
(14, 15)
>>> 14 % 3
2
>>> 14 % 5
4

解は 14 になります。確かに 14 % 3 は 2 に、14 % 5 は 4 になります。解を合同式で表すと \(x \equiv 14 \pmod {15}\) になります。

●中国剰余定理の一般化

中国の剰余定理は合同式の本数を増やしても成立します。

Sympy の関数 crt() は一般化に対応しています。実際に試してみましょう。

●互いに素でない場合

次は、中国剰余定理を整数 \(n_i \ (i = 1, 2, \ldots, k)\) が互いに素でない場合に拡張します。

\(\mathrm{lcm}(m, n)\) は \(m\) と \(n\) の最小公倍数を表します。\(m\) と \(n\) が互いに素であれば \(\gcd(m, n) = 1\) になるので、必要十分条件を満たします。

もちろん、合同式の本数を増やしても成立します。

証明は互いに素の場合と同様 (数学的帰納法) にできるので省略します。

それでは実際に試してみましょう。

>>> sy.ntheory.modular.crt([3,5],[2,4])
(14, 15)
>>> sy.ntheory.modular.crt([30,50],[20,40])
(140, 150)
>>> 140 % 30
20
>>> 140 % 20
0
>>> 140 % 50
40
>>> sy.ntheory.modular.crt([3,5,7],[2,3,2])
(23, 105)
>>> sy.ntheory.modular.crt([30,50,70],[20,30,20])
(230, 1050)
>>> 230 % 30
20
>>> 230 % 50
30
>>> 230 % 70
20

>>> sy.ntheory.modular.crt([3,5,7,11],[2,3,2,4])
(653, 1155)
>>> sy.ntheory.modular.crt([30,50,70,110],[20,30,20,40])
(6530, 11550)
>>> 6530 % 30
20
>>> 6530 % 50
30
>>> 6530 % 70
20
>>> 6530 % 110
40

●位数と原始根

互いに素となる整数 \(a\) と \(n\) に対して、合同式 \(a^k \equiv 1 \pmod n\) を満たす最小の正整数 \(k\) を、\(n\) を法とする \(a\) の位数 (order of a modulo n) といい、\(ord_n(a)\) や \(O_n(a)\) と記述します。オイラーの定理 \(a^{\varphi(n)} \equiv 1 \pmod n\) より、位数 \(k\) は \(\varphi(n)\) 以下になります。そして、\(\varphi(n)\) と位数が等しいとき、\(a\) を \(n\) の原始根 (primitive root of n) と呼びます。

簡単な例を示しましょう。\(2^k \mod {11}, \ 3^k \mod {11}, \ (k = 1, 2, \ldots, 10)\) を計算します。

>>> for k in range(1, 11): print(2 ** k % 11, end=" ")
...
2 4 8 5 10 9 7 3 6 1
>>> for k in range(1, 11): print(3 ** k % 11, end=" ")
...
3 9 5 4 1 3 9 5 4 1

mod 11 の世界では、2 の位数は 10 で、3 の位数は 5 になりました。n が素数のとき \(\varphi(n) = n - 1\) になるので、\(\varphi(11)\) は 10 になります。したがって、2 は 11 の原始根ですが、3 は 11 の原始根ではありません。それから、2 ** k % 11 (k = 1, ..., 10) の計算結果を見ると、1 から 10 までの値がひとつずつ現れていますね。これは原始根で一般的に成立する性質 (定理) です。

\(n\) が素数のとき、\(n\) の原始根 \(a\) が必ず存在することが知られています。これを「原始根の存在定理」とか「原始根定理」といいます。\(n\) の原始根が存在するとき、その個数は \(\varphi(n - 1)\) になります。

それから、位数には次の性質 (定理) があります。

たとえば、mod 11 における 3 の位数は 5 ですが、3k % 11 (k = 1, 2, ..., 10) を計算すると、k = 5 * 2 = 10 のときも 1 になります。

●ウィルソンの定理

位数と原始根を使うと、ウィルソンの定理 (Wilson's theorem) を簡単に証明することができます。

実際に試してみましょう。

>>> for x in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
...     print(x, fact(x - 1) % x)
...
2 1
3 2
5 4
7 6
11 10
13 12
17 16
19 18
23 22
29 28
31 30
37 36
41 40
43 42
47 46

確かに (n - 1)! % n は n - 1 (-1 % n = n - 1) になります。n = 2 の場合、定理が成り立つのは自明なので、奇素数の場合について証明します。

●原始根を求める

位数や原始根を求める場合、次の定理を使うと簡単です。

SymPy には位数を求める関数 n_order() や原始根を判定する is_primitive_root() が用意されています。簡単な使用例を示します。

>>> sy.n_order(2, 11)
10
>>> sy.n_order(3, 11)
5
>>> sy.n_order(2, 13)
12
>>> sy.n_order(3, 13)
3
>>> sy.n_order(4, 13)
6
>>> sy.n_order(5, 13)
4
>>> sy.n_order(6, 13)
12
>>> sy.n_order(7, 13)
12
>>> sy.is_primitive_root(2, 11)
True
>>> sy.is_primitive_root(3, 11)
False
>>> sy.is_primitive_root(2, 13)
True
>>> sy.is_primitive_root(3, 13)
False
>>> sy.is_primitive_root(4, 13)
False
>>> sy.is_primitive_root(5, 13)
False
>>> sy.is_primitive_root(6, 13)
True
>>> sy.is_primitive_root(7, 13)
True

SymPy の関数 primitive_root(p) は、存在するときに限り p の最小の原始根を一つ返します。簡単な使用例を示します。

>>> for x in [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
...     print(x, sy.primitive_root(x))
...
3 2
5 2
7 3
11 2
13 2
17 3
19 2
23 5
29 2
31 3
37 2
41 6
43 3
47 5

Copyright (C) 2024 Makoto Hiroi
All rights reserved.

[ Home | Math ]