式(1)を連立一次ディオファントス方程式と言う。これの、効率の良い、しかも易しい解法を紹介する。
以下、これを文献[1]とする。
ダウンロード: http:lde.pdf
主なアイデアは Gilbert and Pathria の記事[2]から来ているが、改良が加えられている。
この解法は、筆者の知る限り、どの本にも載っていないし、またネット検索でも見つからない。
素朴に計算すると、嫌になるような計算が、巧みに避けられている。
1次ディオファントス方程式にチャレンジした経験のある方は是非とも読んでみてください。
新しい解法の威力が分かると思います。
読者としては、行列の初等的な知識を持っていることが想定されています。
http://ar.nyx.link/lde/lde.pdf
https://www.math.uwaterloo.ca/~wgilbert/Research/GilbertPathria.pdf
1990
この表に対して次の変形規則を導入する。(これを基本変形と言う)
行の変形規則: 1行目から$m$行目について
(a) 任意の行$i$に、$i$ と異なる任意の行の整数倍を加えてよい
(b) 任意の行は符号を反転してもよい
(c) 任意の$2$つの行は交換してもよい
列の変形規則: 1列目から$n$列目について行と同様な操作が許される。
変形の目標は $A$ の部分を対角化することである。対角化すると次のようになるであろう。
\[
\begin{equation}
\begin{array}{rrrr|r}
\hat a_{1,1} & 0 & \cdots & 0 & \hat b_1\\
0 & \hat a_{2,2} & \cdots & 0 & \hat b_2 \\
\cdots & \cdots & \cdots & \cdots & \cdots\\
0 & 0 & \cdots & 0 & \hat b_m \\
\hline
\hat e_{1,1} & \hat e_{1,2} & \cdots & \hat e_{1,n} \\
\hat e_{2,1} & \hat e_{2,2} & \cdots & \hat e_{2,n} \\
\cdots & \cdots & \cdots & \cdots \\
\hat e_{n,1} & \hat e_{n,2} & \cdots & \hat e_{n,n}
\end{array}
\end{equation}
\]
ここに $\hat a_{i,i},\hat b_{i}, \hat e_{i,j}$ などは整数である。
特に $\hat a_{i,i}$ は $i=1,...,k$ で正整数、$i=k+1,...,m$ で $0$ になるようにできる。
なお後に述べる単因子定理によれば、$\hat a_{i+1,i+1}$ は $\hat a_{i,i}$ で割り切れるようになるまで変形できるが、今の問題ではそこまで変形する必要はない。
対角化するために $A$ を次の形にもっていく。これを $A^{(1)}$としよう。
\[
\begin{equation}
\begin{array}{ccccc}
\hat a_{1,1} & 0 & 0 & \cdots & 0\\
0 & * & * & \cdots & *\\
0 & * & * & \cdots & *\\
0 & * & * & \cdots & *\\
\end{array}
\end{equation}
\]
ここに $*$ は何かの整数である。これができれば残った $*$ の部分を同様に処理すればよい。
文献[1]にはそのための手順が書かれているが、ここでは Maxima に適した方法を紹介する。
STEP1: $A$ の中の $0$ でない絶対値の最小の要素を $(1,1)$ の位置に移動する。
行と列の交換で達成できる。
STEP2: $1$ 列目の $(1,k) \;(k=2,..,m)$ を $0$ にすべく基本変形を繰り返す。
STEP3: $1$ 行目の $(k,1) \;(k=2,..,m)$ を $0$ にすべく基本変形を繰り返す。
以上のステップを繰り返すと式(7)の $*$ の部分が減少して行き、最終的には式(6)の形にまで行き着く。
文献[1]との違いは、文献[1]では行の処理を先に行っているが、ここでは列の処理を先に行っている。
また STEP1 では $A$ の全体を対象にしているところが違う。(この方が実行が早いし、プログラムも簡単になる)
正確には Maxima のプログラムコードを参照すべし。
\[
\begin{equation}
\hat A=
\begin{pmatrix}
\hat a_{1,1} & 0 & \cdots & 0 \\
0 & \hat a_{2,2} & \cdots & 0 \\
\cdots & \cdots & \cdots & \cdots \\
0 & 0 & \cdots & 0 \\
\end{pmatrix}
,\quad
\hat{\boldsymbol b}=
\begin{pmatrix}
\hat b_1 \\
\hat b_2 \\
\cdots \\
\hat b_m \\
\end{pmatrix}
,\quad
\hat E=\begin{pmatrix}
\hat e_{1,1} & \hat e_{1,2} & \cdots & \hat e_{1,n} \\
\hat e_{2,1} & \hat e_{2,2} & \cdots & \hat e_{2,n} \\
\cdots & \cdots & \cdots & \cdots \\
\hat e_{n,1} & \hat e_{n,2} & \cdots & \hat e_{n,n}
\end{pmatrix}
\end{equation}
\]
と置くと次の方程式
\[
\begin{equation}
\hat A \hat{\boldsymbol x}=\hat{\boldsymbol b}
\end{equation}
\]
を解く問題に帰着する。$\hat{\boldsymbol x}$ についてのこの方程式は$\hat A$ が対角化されているので容易に求められる。${\boldsymbol x}$ は
\[
\begin{equation}
{\boldsymbol x}=\hat E \hat{\boldsymbol x}
\end{equation}
\]
で求められる。
\[
\begin{array}{rrr|r}
5 & 6 & 8 & 1 \\
6 & -11 & 7 & 9 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|r}
5 & 6 & 8 & 1 \\
1 & -17 & -1 & 8 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|r}
1 & -17 & -1 & 8 \\
5 & 6 & 8 & 1 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|r}
1 & -17 & -1 & 8 \\
0 & 91 & 13 & -39 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\\
\Longrightarrow
\begin{array}{rrr|r}
1 & 0 & 0 & 8 \\
0 & 91 & 13 & -39 \\
\hline
1 & 17 & 1 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|r}
1 & 0 & 0 & 8 \\
0 & 13 & 91 & -39 \\
\hline
1 & 1 & 17 & \\
0 & 0 & 1 & \\
0 & 1 & 0 &
\end{array}
\Longrightarrow
\begin{array}{rrr|r}
1 & 0 & 0 & 8 \\
0 & 13 & 0 & -39 \\
\hline
1 & 1 & 10 & \\
0 & 0 & 1 & \\
0 & 1 & -7 &
\end{array}
\]
従って
\[
\begin{equation}
\hat A=\begin{pmatrix}
1 & 0 & 0 \\
0 & 13 & 0
\end{pmatrix}
,\quad
\hat{\boldsymbol b}=\begin{pmatrix}
8 \\
-39
\end{pmatrix}
,\quad
\hat E=\begin{pmatrix}
1 & 1 & 10 \\
0 & 0 & 1 \\
0 & 1 & -7
\end{pmatrix}
\end{equation}
\]
であり、式(9)と式(10)により
\[
\begin{equation}
\hat{\boldsymbol x}=\begin{pmatrix}
8 \\
-3 \\
\hat x_3
\end{pmatrix}
,\quad
{\boldsymbol b}=\hat E \hat{\boldsymbol b}=\begin{pmatrix}
5+10\hat x_3 \\
\hat x_3 \\
-3 - 7\hat x_3
\end{pmatrix}
\end{equation}
\]
として式(3)の結果が得られる。ここに $\hat x_3$ は任意の整数であり、式(3)では $k$ になっている。
Maxima のプログラム
http:lde.mac
を紹介する。
Maxima の会話的実行は既知とする。
Maxima を立ち上げ
directory("");
batchload("lde.mac");
(%i3) M:matrix([5,6,8],[6,-11,7]); b:[1,9]; (%o3) matrix([5,6,8],[6,-11,7]) (%o4) [1,9] (%i5) lde(M,b); step0 0 [matrix([1],[9]),matrix([5,6,8],[6,-11,7]), matrix([1,0,0],[0,1,0],[0,0,1])] step1 1 [matrix([1],[9]),matrix([5,6,8],[6,-11,7]), matrix([1,0,0],[0,1,0],[0,0,1])] step2 1 [matrix([1],[8]),matrix([5,6,8],[1,-17,-1]), matrix([1,0,0],[0,1,0],[0,0,1])] step1 1 [matrix([8],[1]),matrix([1,-17,-1],[5,6,8]), matrix([1,0,0],[0,1,0],[0,0,1])] step2 1 [matrix([8],[-39]),matrix([1,-17,-1],[0,91,13]), matrix([1,0,0],[0,1,0],[0,0,1])] step1 1 [matrix([8],[-39]),matrix([1,-17,-1],[0,91,13]), matrix([1,0,0],[0,1,0],[0,0,1])] step3 1 [matrix([8],[-39]),matrix([1,0,0],[0,91,13]), matrix([1,17,1],[0,1,0],[0,0,1])] step1 1 [matrix([8],[-39]),matrix([1,0,0],[0,91,13]), matrix([1,17,1],[0,1,0],[0,0,1])] step1 2 [matrix([8],[-39]),matrix([1,0,0],[0,13,91]), matrix([1,1,17],[0,0,1],[0,1,0])] step3 2 [matrix([8],[-39]),matrix([1,0,0],[0,13,0]), matrix([1,1,10],[0,0,1],[0,1,-7])] step1 2 [matrix([8],[-39]),matrix([1,0,0],[0,13,0]), matrix([1,1,10],[0,0,1],[0,1,-7])] [matrix([8],[-39]),matrix([1,0,0],[0,13,0]),matrix([1,1,10],[0,0,1],[0,1,-7])] (%o5) [10*x3+5,x3,(-7*x3)-3] (%i6)最後の出力
[10*x3+5,x3,(-7*x3)-3]
プログラムは式(6)に至るまでの中間結果も出力する。
ステップごとの表は
\[
\begin{pmatrix}
A^{(l)}&{\boldsymbol b}^{(l)} \\
E^{(l)}&O
\end{pmatrix}
\]
の形をしている。$O$ は式(5)から式(6)に至る表では空欄であるが、それを形式的に $O$ で表している。
各ステップでは ${\boldsymbol b}^{(l)},A^{(l)},E^{(l)}$ がこの順でリスト形式で表示されている。
例えば
[matrix([8],[-39]),matrix([1,0,0],[0,13,0]), matrix([1,1,10],[0,0,1],[0,1,-7])]
lde.mac
の主関数の一つ lde(M,b)
の定義を次に示す。
列と行変形の基本的なアルゴリズムが正確に解るはずである。詳しくは lde.mac
を直接参照されたい。
lde(M,b):=block([B,L,n,k], B:tab_gen2(M,b), if B = false then return("# ERROR,inconsistent b"), n:min(length(M),length(M[1])), dprint("step0",0,B), /* don't replace B[2] below with A, which behaves differently! */ for k:1 while k <= n and chk_zero(B[2],k)=false do ( X:step1(B,k), if X=false then (k:n+1, return(false)), B:X, dprint("step1",k,B), loop, unless chk_col(B[2],k) do ( B:step2(B,k), dprint("step2",k,B), B:step1(B,k), dprint("step1",k,B) ), unless chk_row(B[2],k) do ( B:step3(B,k), dprint("step3",k,B), B:step1(B,k), dprint("step1",k,B) ), if chk_col(B[2],k)=false then go(loop) ), print(B), L:lde_solution(B), if lde_final(L,M,b) then return(expand(L)) else return("no solution") );
B
は3つの行列 S,A,T
を要素とするリストである。tab_gen2(M,b)
によってそのようなリストが生成される。S
はリスト b
から生成される1列から成る行列、A
は M
のコピー、T
はM
を基にした単位行列(M
の行数と列数の小さい方を採用する)である。S,A,T
は基本変形によって変形を受ける。dprint("step2",k,B)
k
は処理中の行番号(同じことであるが処理中の列番号)である。
lde(M,b)
の内部コードは行列 $M$ の単因子標準形を求めるコードと多くを共有している。
SymPy のプログラム
http:mlde.py
を紹介する。
名称が lde.py
ではなく、mlde.py
としたのは、モジュール化した時に中に含まれている関数 lde
がモジュール名と一致していることが何かと不便なので、ファイル名に "m
" を添えたのである。
import os os.getcwd()
import mlde from mlde import *
>>> M=Mat([5,6,8],[6,-11,7]); b=[1,9] >>> lde(M,b) step0 0 [Matrix([ [1], [9]]), Matrix([ [5, 6, 8], [6, -11, 7]]), Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]])] step1 1 [Matrix([ [1], [9]]), Matrix([ [5, 6, 8], [6, -11, 7]]), Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]])] 中略 step1 2 [Matrix([ [ 8], [-39]]), Matrix([ [1, 0, 0], [0, 13, 0]]), Matrix([ [1, 1, 10], [0, 0, 1], [0, 1, -7]])] [10*x3 + 5, x3, -7*x3 - 3] >>>SymPy における行列は正式には
Matrix
であり、Maxima の matrix
に比べて引数の与え方が少し複雑である。matrix
で行列を表すにはmatrix(行1,行2,...)
Matrix
ではMatrix([行1,行2,...])
[ ]
" が余分に必要である。Matrix
の使い方を柔軟にするためである。mlde
では新たに関数 Mat
を定義して、用法を matrix
に合わせている。
Maxima と Python の違いの一つに、リストや行列のインデックスの開始番号がある。Maxima は 1 から始めるが Python は 0 から始める。 Maxima から Python への翻訳において、最も間違えやすい部分である。
mlde.py
の主関数の一つ lde(M,b)
の定義を次に示す。mlde.py
を直接参照されたい。def lde(M,b): B = tab_gen2(M,b) if B == False: error("# ERROR,inconsistent b") n = min(M.shape) dprint("step0",-1,B) # don't replace B[1] below with A, which behaves differently! for k in range(0,n): if chk_zero(B[1],k)==True: break X = step1(B,k) if X == False: break B = X dprint("step1",k,B) while True: while chk_col(B[1],k)==False: B = step2(B,k) dprint("step2",k,B) B = step1(B,k) dprint("step1",k,B) while chk_row(B[1],k)==False: B = step3(B,k) dprint("step3",k,B) B = step1(B,k) dprint("step1",k,B) if chk_col(B[1],k)==True: break L = lde_solution(B) if lde_final(L,M,b) : return list(expand(L)) else: return "no solution"
B
は3つの行列 S,A,T
を要素とするリストである。tab_gen2(M,b)
によってそのようなリストが生成される。S
はリスト b
から生成される1列から成る行列、A
は M
のコピー、T
はM
を基にした単位行列(M
の行数と列数の小さい方を採用する)である。S,A,T
は基本変形によって変形を受ける。dprint("step2",k,B)
k
は処理中の行番号(同じことであるが処理中の列番号)である。
出力は Maxima 版と完全に同じになるように調整されている。
lde(M,b)
の内部コードは行列 $M$ の単因子標準形を求めるコードと多くを共有している。
lde.mac
には整数行列 $M$ の単因子標準形を求めるコードが含まれている。
多項式行列の単因子標準形も同じ考え方で求められるが、実際に必要になるコードは遥かに複雑であり、lde.mac
ではサポートされていない。elmdiv.mac
でサポートされている。
従って対角要素は $d_1,d_2,...,0,0,...$ の形をとる。$0,0,...$ は発生しない場合もある。
$\hat M$ を $M$ の単因子標準形と言う。
例えば $ M=\begin{pmatrix}
5&6&8 \\
6&-11&7
\end{pmatrix} $ は $ S=\begin{pmatrix}-1&1\\6&-5\end{pmatrix},
\quad T=\begin{pmatrix}1&1&10\\0&0&1\\0&1&-7\end{pmatrix} $ によって単因子標準形 $ SMT=\hat M= \begin{pmatrix}1&0&0 \\ 0&13&0 \end{pmatrix} $ に変換される。
行の変形規則:
(a) 任意の行$i$に、$i$ と異なる任意の行の整数倍を加えてよい
(b) 任意の行は符号を反転してもよい
(c) 任意の$2$つの行は交換してもよい
列の変形規則: 行と同様な操作が許される。
行の変形規則と列の変形規則を合わせて整数行列の基本変形という。これらは可逆な変形規則であり、正則な整数行列で表現できる。
変形の目標は $M$ を単因子標準形になるよう対角化することである。最初の目標は\[
\begin{equation}
\left(
\begin{array}{ccccc}
d_1&0&0&\cdots&0\\
0&*&*&\cdots&*\\
0&*&*&\cdots&*\\
0&*&*&\cdots&*\\
\end{array}
\right)
\end{equation}
\]の形に持っていくことである。ここに $*$ の部分は全て $d_1$ の倍数とする。これに成功すれば $d_2,d_3,...$ と進んで行けばよい。ディオファントス方程式を解く場合には単に対角化すればよかったが単因子標準形の場合にはそれでは不十分である。
以下では単因子標準形への変換行列も同時に求める方法を紹介する。すなわち $SMT$ が単因子標準形 $\hat M$ となる正則行列 $S$ と $T$ をも求める。この場合には次のようにやればよい。
$M$ を $n\times m$ の行列とする。その下で $(n+m)\times(n+m)$ の正方行列 \[
\begin{pmatrix} M&E_n \\ E_m&O \end{pmatrix} \] を作る。ここに $E_n$ は $n\times n$ の単位行列、$E_m$ は $m\times m$ の単位行列である。$O$ は $m\times n$ の零行列である。
基本変形は $O$ の部分に作用しないように範囲を限定する。従って行変形は $1$ から $n$ に、列変形は $1$ から $m$ に限定する。その下で変形を繰り返して対角化を進めるのである。最終的な結果は $$ \begin{pmatrix} \hat M&S \\ T&O \end{pmatrix} $$ になっている。
例えば $M=\begin{pmatrix}
5&6&8 \\
6&-11&7
\end{pmatrix}$ であれば \[
\begin{pmatrix}
5&6&8&1&0\\
6&-11&7&0&1\\
1&0&0&0&0\\
0&1&0&0&0\\
0&0&1&0&0
\end{pmatrix} \]を作るのであるが、実際の手計算では、これを
\[
\begin{array}{rrr|rr}
5&6&8&1&0 \\
6&-11&7&0&1 \\
\hline
1&0&0&\\
0&1&0&\\
0&0&1 &
\end{array}
\]と書き換えた方がわかりやすいであろう。
\[
\begin{array}{rrr|rr}
5 & 6 & 8 & 1 & 0 \\
6 & -11 & 7 & 0 & 1 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|rr}
5 & 6 & 8 & 1 & 0 \\
1 & -17 & -1 & -1 & 1 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|rr}
1 & -17 & -1 & -1 & 1 \\
5 & 6 & 8 & 1 & 0 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|rr}
1 & -17 & -1 & -1 & 1 \\
0 & 91 & 13 & 6 & -5 \\
\hline
1 & 0 & 0 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\\
\Longrightarrow
\begin{array}{rrr|rr}
1 & 0 & 0 & -1 & 1 \\
0 & 91 & 13 & 6 & -5 \\
\hline
1 & 17 & 1 & \\
0 & 1 & 0 & \\
0 & 0 & 1 &
\end{array}
\Longrightarrow
\begin{array}{rrr|rr}
1 & 0 & 0 & -1 & 1 \\
0 & 13 & 91 & 6 & -5 \\
\hline
1 & 1 & 17 & \\
0 & 0 & 1 & \\
0 & 1 & 0 &
\end{array}
\Longrightarrow
\begin{array}{rrr|rr}
1 & 0 & 0 & -1 & 1 \\
0 & 13 & 0 & 6 & -5 \\
\hline
1 & 1 & 10 & \\
0 & 0 & 1 & \\
0 & 1 & -7 &
\end{array}
\]
これから
\[
S=\begin{pmatrix}
-1 & 1 \\
6 & -5
\end{pmatrix}
,\quad
\hat M=\begin{pmatrix}
1 & 0 & 0\\
0 & 13 & 0
\end{pmatrix}
,\quad
T=\begin{pmatrix}
1 & 1 & 10\\
0 & 0 & 1 \\
0 & 1 & -7
\end{pmatrix}
\]
を得る。
連立1次ディオファントス方程式の解法との類似は明瞭である。すなわち ${\boldsymbol b}$ が $S$ に、$E$ が $T$ に置き換わったに過ぎない。なお文献[1]での $S^{(1)}S^{(2)}\cdots$ が、ここでは $S$ で、$T^{(1)}T^{(2)}\cdots$ が $T$ とされている。
対角化のアルゴリズムを正確に自然言語で説明することは難しい。正確なアルゴリズムはプログラムコードを見るのが一番良い。
行列 $M=\begin{pmatrix}5&6&8\\ 6&-11&7\end{pmatrix}$ の単因子標準形 $\hat M$ を求める。
(%i49) M:matrix([5,6,8],[6,-11,7]); (%o49) matrix([5,6,8],[6,-11,7]) (%i50) elmdiv(M); step0 0 [matrix([1,0],[0,1]),matrix([5,6,8],[6,-11,7]), matrix([1,0,0],[0,1,0],[0,0,1])] step1 1 [matrix([1,0],[0,1]),matrix([5,6,8],[6,-11,7]), matrix([1,0,0],[0,1,0],[0,0,1])] step2 1 [matrix([1,0],[-1,1]),matrix([5,6,8],[1,-17,-1]), matrix([1,0,0],[0,1,0],[0,0,1])] 中略 step1 2 [matrix([-1,1],[6,-5]),matrix([1,0,0],[0,13,0]), matrix([1,1,10],[0,0,1],[0,1,-7])] (%o50) [matrix([-1,1],[6,-5]),matrix([1,0,0],[0,13,0]), matrix([1,1,10],[0,0,1],[0,1,-7])]ここで得られた
matrix([-1,1],[6,-5]) matrix([1,0,0],[0,13,0]) matrix([1,1,10],[0,0,1],[0,1,-7])
(%i51) M:matrix([4,3,6,2],[2,3,6,4],[6,6,13,5]); (%o51) matrix([4,3,6,2],[2,3,6,4],[6,6,13,5]) (%i52) elmdiv(M); step0 0 [matrix([1,0,0],[0,1,0],[0,0,1]), matrix([4,3,6,2],[2,3,6,4],[6,6,13,5]), matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])] step1 1 [matrix([1,0,0],[0,1,0],[0,0,1]),matrix([2,3,6,4],[4,3,6,2],[5,6,13,6]), matrix([0,0,0,1],[0,1,0,0],[0,0,1,0],[1,0,0,0])] step2 1 [matrix([1,0,0],[-2,1,0],[-2,0,1]), matrix([2,3,6,4],[0,-3,-6,-6],[1,0,1,-2]), matrix([0,0,0,1],[0,1,0,0],[0,0,1,0],[1,0,0,0])] 中略 step1 3 [matrix([-2,0,1],[8,1,-4],[13,1,-6]), matrix([1,0,0,0],[0,1,0,0],[0,0,6,0]), matrix([0,0,0,1],[0,1,-2,-4],[0,-1,3,1],[1,1,-3,1])] (%o52) [matrix([-2,0,1],[8,1,-4],[13,1,-6]), matrix([1,0,0,0],[0,1,0,0],[0,0,6,0]), matrix([0,0,0,1],[0,1,-2,-4],[0,-1,3,1],[1,1,-3,1])]ここで得られた
matrix([-2,0,1],[8,1,-4],[13,1,-6]) matrix([1,0,0,0],[0,1,0,0],[0,0,6,0]) matrix([0,0,0,1],[0,1,-2,-4],[0,-1,3,1],[1,1,-3,1])
lde.mac
の主関数の一つ elmdiv(M)
の定義を次に示す。lde.mac
を直接参照されたい。elmdiv(M):=block([B,n,k,r], n:min(length(M),length(M[1])), B:tab_gen(M), dprint("step0",0,B), /* don't use 'for' loop */ k:1, /* don't replace B[2] with A, which behaves differently! */ while k <= n and chk_zero(B[2],k)=false do ( X:step1(B,k), if X=false then (k:n+1, return(false)), B:X, dprint("step1",k,B), loop, /* we should avoid go(loop) if possible. this is merely an example for go(loop) */ unless chk_col(B[2],k) do ( B:step2(B,k), dprint("step2",k,B), B:step1(B,k), dprint("step1",k,B) ), unless chk_row(B[2],k) do ( B:step3(B,k), dprint("step3",k,B), B:step1(B,k), dprint("step1",k,B) ), if chk_col(B[2],k)=false then go(loop), /* we may assume here both chk_col(B[2],k) and chk_row(B[2],k) are true */ if chk_mod(B[2],k) then k:k+1 else ( X:step4(B,k), if X=false then k:n+1 else ( B:X, dprint("step4",k,B) ) ) ), r:chk_final(B,M), if r # 0 then error("# ERROR: bug!",r), return(B) );
step4(B,k)
が追加されたことになる。
行列 $M=\begin{pmatrix}5&6&8\\ 6&-11&7\end{pmatrix}$ の単因子標準形 $\hat M$ を求める。
>>> M=Mat([5,6,8],[6,-11,7]) >>> elmdiv(M) step0 0 [Matrix([ [1, 0], [0, 1]]), Matrix([ [5, 6, 8], [6, -11, 7]]), Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]])] step1 1 [Matrix([ [1, 0], [0, 1]]), Matrix([ [5, 6, 8], [6, -11, 7]]), Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]])] 中略 step1 2 [Matrix([ [-1, 1], [ 6, -5]]), Matrix([ [1, 0, 0], [0, 13, 0]]), Matrix([ [1, 1, 10], [0, 0, 1], [0, 1, -7]])] [Matrix([ [-1, 1], [ 6, -5]]), Matrix([ [1, 0, 0], [0, 13, 0]]), Matrix([ [1, 1, 10], [0, 0, 1], [0, 1, -7]])] >>>ここで得られた
Matrix([[-1,1],[6,-5]]) Matrix([[1,0,0],[0,13,0]]) Matrix([[1,1,10],[0,0,1],[0,1,-7]])
>>> M=Mat([4,3,6,2],[2,3,6,4],[6,6,13,5]) >>> elmdiv(M) step0 0 [Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]), Matrix([ [4, 3, 6, 2], [2, 3, 6, 4], [6, 6, 13, 5]]), Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])] step1 1 [Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]), Matrix([ [2, 3, 6, 4], [4, 3, 6, 2], [5, 6, 13, 6]]), Matrix([ [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0]])] 中略 step1 3 [Matrix([ [-2, 0, 1], [ 8, 1, -4], [13, 1, -6]]), Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 6, 0]]), Matrix([ [0, 0, 0, 1], [0, 1, -2, -4], [0, -1, 3, 1], [1, 1, -3, 1]])] [Matrix([ [-2, 0, 1], [ 8, 1, -4], [13, 1, -6]]), Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 6, 0]]), Matrix([ [0, 0, 0, 1], [0, 1, -2, -4], [0, -1, 3, 1], [1, 1, -3, 1]])] >>>
ここで得られた
Matrix([[-2,0,1],[8,1,-4],[13,1,-6]]) Matrix([[1,0,0,0],[0,1,0,0],[0,0,6,0]]) Matrix([[0,0,0,1],[0,1,-2,-4],[0,-1,3,1],[1,1,-3,1]])
mlde.py
の主関数の一つ elmdiv(M)
の定義を次に示す。mlde.py
を直接参照されたい。def elmdiv(M): n = min(M.shape) B = tab_gen(M) dprint("step0",-1,B) k = 0 # don't replace B[1] with A, which behaves differently! while k < n and chk_zero(B[1],k)==False: X = step1(B,k) if X == False: break B = X dprint("step1",k,B) while True: while chk_col(B[1],k) == False: B = step2(B,k) dprint("step2",k,B) B = step1(B,k) dprint("step1",k,B) while chk_row(B[1],k) == False: B = step3(B,k) dprint("step3",k,B) B = step1(B,k) dprint("step1",k,B) if chk_col(B[1],k) == True: break # we may assume here both chk_col(B[1],k) and chk_row(B[1],k) are true if chk_mod(B[1],k): k += 1 else: X = step4(B,k) if X==False: break B = X dprint("step4",k,B) r = chk_final(B,M) if r != 0 : error("# ERROR: bug!",r) return B
step4(B,k)
が追加されたことになる。
教材を作るのに、行列式が $\pm 1$ の整数行列 $P$ をランダムに生成できると何かと良い。
例えば与えられた Jordan 行列を $J$ とすると $P^{-1}JP$ によって、$J$ と相似な整数行列を作ることができる。
lde.mac
および mlde.py
には
mat_rand(n,m,nr)
n
は正方行列のサイズ、m
と nr
には適当な正整数を与えればよい。m
が大きいと、生成される行列要素も大きくなる傾向がある。nr
が大きいとランダム性が増す。