GAP (Groups, Algorithms, Programming, 外部サイト) は無料で利用できる高性能な数学ソフトウェアで、特に代数学関係の計算に強い。 GAP には多くの組み込み関数が用意されておリ、単にそれを呼び出して使うだけでも十分利用価値がある。 ここでは GAP を組み込み関数を呼び出して使う方法について解説する。 この方法以外に GAP では自分でプログラムを書くことができる。 プログラミングについては第 3 回で解説する。
GAP のマニュアルは 1000 ページを超え、 pdf ファイルが /usr/share/gap/doc/ にある。 (GAP の本家からダウンロードしてインストールした場合には html ファイルもあるが、 Knoppix DVD には pdf ファイルしかないようである。 ここにコピーを置いたので見るといいだろう。) もちろん、ここではそのすべてを説明するわけではない。 日本語の参考文献は多くはないが
GAP は常に厳密計算を行うので、通常の方法では近似計算などはできない。 また扱える数にも制限が強い。 近似計算やより柔軟な数の扱いが必要なときには、あとで学ぶ maxima の方が適しているであろう。
GAP を起動するには左下のメニューから「Math」→「GAP」とすればよい。 しかしこの方法だと作業ディレクトリの指定ができず、データの読み書きを USB メモリから行うには不便である。 以下の方法を推奨する。
knoppix@Microknoppix:~$ cd /media/sdc1/gap knoppix@Microknoppix:/media/sdc1/gap$ gap ######### ###### ########### ### ############# ###### ############ #### ############## ######## ############# ##### ############### ######## ##### ###### ##### ###### # ######### ##### ##### ###### ###### ########## ##### ##### ####### ##### ##### #### ##### ###### ######## #### ##### ##### ############# ### #### ##### ####### #### #### ########### #### #### ##### ####### ##### ##### ###### #### #### ##### ####### ##### ##### ##### ############# ##### ##### ################ ##### ############# ###### ##### ################ ##### ############# ################ ################## ##### #### ############### ##### ##### ##### #### ############# ##### ##### ##### #### ######### ##### ##### ##### #### Information at: http://www.gap-system.org Try '?help' for help. See also '?copyright' and '?authors' Loading the library. Please be patient, this may take a while. GAP4, Version: 4.4.12 of 17-Dec-2008, i486-pc-linux-gnu-i486-linux-gnu-gcc Components: small 2.1, small2 2.0, small3 2.0, small4 1.0, small5 1.0, small6 1.0, small7 1.0, small8 1.0, small9 1.0, small10 0.2, id2 3.0, id3 2.1, id4 1.0, id5 1.0, id6 1.0, id9 1.0, id10 0.1, trans 1.0, prim 2.1 loaded. Packages: CTblLib 1.1.3, TomLib 1.1.4 loaded. gap>
gap> quit; knoppix@Microknoppix:/media/sdc1/gap$
GAP では計算式の後に必ず ; (セミコロン) をつける。 セミコロンがない場合には計算式が終了していないと判断され、更なる入力を待つ状態になる。 言い換えれば、長い計算や命令はセミコロンを付けずに複数の行に分けて書くことができる。 式中のスペースは無視されるので、見やすいように適当に挿入して構わない。 また計算結果を出力する必要がない場合には ;; (セミコロン2つ) をつければ、 計算だけをして出力はされない。
gap> 123 + 456; # 加法 579 gap> 321 - 234; # 減法 87 gap> 12 * 23; # 乗法 276 gap> 12^2; # 累乗 - 指数には整数しか指定できない 144 gap> 1 + 2; 3 + 4; # 複数の計算を 1 行に書いてもよい。 3 7 gap> 13 / 4; # 除法 - 計算結果は分数で表示され近似計算は行わない。 13/4 gap> 0.1; # 小数は扱えない。 Syntax error: ; expected 0.1; ^ 1 gap> 2^50; # 累乗 - PC のメモリの許す限り、いくらでも大きな数が扱える。 1125899906842624 gap> 20 mod 3; # 剰余 2 gap> Int(20 / 3); # 商 - Int で与えられた数の整数部分を返す。 6
gap> Gcd(8,12); # 最大公約数 4 gap> Lcm(8,12); # 最小公倍数 24 gap> Binomial(6,2); # 二項係数 15 gap> IsPrime(2^11-1); # 素数判定 false gap> FactorsInt(2^11-1); # 素因数分解 [ 23, 89 ] gap> NextPrimeInt(2^11-1); # 与えられた自然数より大きい最小の素数を返す 2053
gap> ?Gcd Help: Showing `Reference: Gcd' > Gcd( <R>, <r1>, <r2>, ... ) F > Gcd( <R>, <list> ) F > Gcd( <r1>, <r2>, ... ) F > Gcd( <list> ) F In the first two forms `Gcd' returns the greatest common divisor of the ring ... 略 ...のようにヘルプを利用することができる。また
gap> ?Polynomial Help: several entries match this topic - type ?2 to get match [2] [1] Tutorial: Polynomials [2] Reference: Polynomial Factorization [3] Reference: Polynomial Rings ... 略 ...のようにマニュアルの該当個所を表示する場合もある。
gap> Gcd Gcd GcdCoeffs GcdInt GcdOp GcdRepresentation GcdRepresentationOp Gcdex
課題. コマンド Gcdex について、何をする関数であるかをヘルプまたはマニュアルで調べ、実際に試してみよ。
GAP におけるエラーでよくあるのは、以下のようなものである。
gap> a; # 未定義の変数を参照した (変数については後述する) Variable: 'a' must have a value gap> Gcm(2, 4); # 未定義の関数を利用しようとした Variable: 'Gcm' must have a value gap> Gcd(2); # 定義済みの関数であるが、引数の数や型があっていない Error, usage: Gcd( [<R>,] <r1>, <r2>... ) called from <function>( <arguments> ) called from read-eval-loop Entering break read-eval-print loop ... you can 'quit;' to quit to outer loop, or you can 'return;' to continue brk> quit; gap>エラーで停止し「brk>」を表示している場合には、ここでエラーの原因などを調べることができるが、 よく分からない時には「quit;」で抜けることができる。 次もよくあるミスである。
gap> 1 + 1 # ; (セミコロン) の付け忘れ。 > ; # 次の行に ; を入力すればよい。 2誤ったプログラムで無限ループに陥ったときや、時間のかかりすぎるコマンドを実行してしまったときには、 Ctrl+c でプログラムを停止することができ、GAP のプロンプトに戻る。 しかし、場合によっては停止処理に時間がかかり、GAP のプロンプトがなかなか表示されない。 計算を中止してでもプログラムを停止したければ Ctrl+c を繰り返し押す。 これによって GAP 自体が強制終了され、コマンドラインに戻ることになる。
GAP を電卓のように使うとは言っても、計算結果をその後の計算に利用することぐらいはしたいであろう。 このためには変数を用意して、そこに計算結果を代入しておけばよい。 簡単な例を見る。
gap> a := 2^3; 8 gap> b := 3^2; 9 gap> a + b; 17 gap> c = 1; Variable: 'c' must have a value gap> a = 1; false変数への代入は = ではなく := で行う。 = は代入ではなく、左辺と右辺が等しいかを true または false で返す。 変数名は、アルファベットの大文字、小文字、数字、 および _ (アンダースコア)で任意の長さを持つことができる。 アルファベットの大文字と小文字は厳密に区別される。 しかし特定の文字列は GAP が利用するために予約されており、利用することができない。 例えば、true (真)、false (偽)、E (1 のべき根), ER (平方根), Z (有限体の単位元) などは利用できない。 これ以外にも GAP が利用している変数や関数を上書きしてしまうと、思わぬ不具合が生じることがあるので注意しよう。 GAP の予約語や組込み関数の多くはアルファベットの大文字で始まるので、 自分の利用する変数名はアルファベットの小文字で始めるようにするとほとんど問題は起きない。
変数への代入の際に
gap> a := 2;; b := 3;; gap> a := a + b; 5のように、 a の値を使って a に値を代入することも許される。
GAP では直前の計算結果を last という変数に自動的に代入する。 last は上書き可能で、自分で代入することもできるが、次の計算が行われるとその結果が代入されてしまう。
gap> last := 12; 12 gap> last; 12 gap> 1+2; 3 gap> last; 3last という名前の変数は直前の計算結果を利用するときにだけ使えると思っていた方がよいであろう。
GAP を電卓のように利用するときであっても、 データがある程度大きいときにはそれをコマンドラインから入力するのは面倒である。 ここでは GAP に入力するデータを予めファイルに記述しておいて、それを GAP に読み込ませる方法を説明する。 これは、同じデータを繰り返し用いたいときなどにも有効な手段である。 また計算結果などをファイルに書き出す方法も説明する。
GAP にデータを読み込ませるのは簡単である。 まずコマンドラインから入力する内容を記述したファイルを用意する。 この際、説明などのコメントを記述しておくこともできる。 GAP では行の # 以降をコメントとして無視する。 例えば以下の内容のファイルを matrix.gap という名前で用意する。
mat := [[1,2,3], [4,5,6], [7,8,9]]; # 行列 vec := [1,2,3]; # ベクトルこれを GAP に読み込ませるには、GAP のコマンドラインから
gap> Read("matrix.gap"); gap> vec * mat; [ 30, 36, 42 ]とする。読み込むファイルが GAP を実行中のディレクトリにないときには、 ファイルを絶対パス、または相対パスで指定する。 もっと簡単な方法は、入力したい内容をエディタからコピーしてコマンドラインに貼り付けることである。 また GAP 起動時に読み込ませたいのであれば
knoppix@Microknoppix:/media/sdc1/gap$ gap matrix.gapのようにコマンドラインから GAP のオプションとして読み込ませたいファイルを指定すればよい。 この方法では複数のファイルを読み込ませることができる。
課題. 上記の方法で GAP にファイルを読み込ませ、vec と mat にきちんと代入されていることを確認しなさい。
次に計算結果をファイルに保存する方法を説明する。 もっとも簡単な方法は画面に表示される内容をすべてファイルに保存する方法である。
gap> LogTo("logfile"); gap> 1 + 2; 3 gap> LogTo();LogTo に保存するファイル名を指定すると保存が開始される。 このあと LogTo() とするまでの間、画面に表示されるものと同じ内容がすべてファイルに書き出される。 とりあえず、すべてを保存しておき、あとで必要な部分を整理する場合に便利であろう。
課題. LogTo を試してみなさい。
次に説明する方法は必要なデータのみをその出力形式まで指定して保存する。 工夫すれば出力を LaTeX の形式で保存するなど、いろいろなことができる。
gap> a := 4;; b := 6;; gap> Print("Gcd(", a, ", ", b, ") = ", Gcd(a, b), "\n"); Gcd(4, 6) = 2 gap> PrintTo("logfile", "Gcd(", a, ", ", b, ") = ", Gcd(a, b), "\n"); gap> AppendTo("logfile", 1 + 2, "\n");Print は複数の値を , (カンマ) で区切って指定でき、画面にそれを出力する。 文字列は " " でくくって指定する。 上の例では "Gcd(" を文字列として出力させた後、変数 a の値、 文字列 ", " などを指定している。 最後の "\n" は改行を表す特殊記号である。 PrintTo は Print と同じ書式で、出力をはじめの引数に指定したファイルに対して行う。 この例では logfile に書き出す。 PrintTo は指定した名前のファイルが存在するとき、すでに書き込んである内容をすべて消して書き込んでしまうので注意が必要である。 すでに書いてある内容を消さずに、出力を書き足すときには AppendTo を使う。
課題. PrintTo と AppendTo を試してみなさい。
ベクトルは [1,0,1] のように [] でくくって記述する。 すなわち、スカラーを成分とするリストを行列と見る。 加法やスカラー倍は以下のように自然な方法で記述できる。
gap> [0,1,0] + [1,2,3]; [ 1, 3, 3 ] gap> 2 * [2,3,4]; [ 4, 6, 8 ] gap> [1,2] + [1,0,0]; [ 2, 2, 0 ]三つ目の例のように次元の異なるベクトルの加法も許される。 行列は 2 次元配列、すなわち行ベクトルを成分とするリスト、で表される。
gap> A := [[1,0],[0,1]];; gap> A^2; [ [ 1, 0 ], [ 0, 1 ] ] gap> A^(-1); [ [ 1, 0 ], [ 0, 1 ] ] gap> B := [[1,1],[1,1]];; gap> B^(-1); fail加法やスカラー倍はベクトルと同様である。 また行列の積や累乗も自然に計算できる。 逆行列は Inverse(A) として求めることもできるが A^(-1) でもよい。 正則でない行列 B に対しては B^(-1) は fail となる。
行列の固有値や固有ベクトルは以下のように求めることができる。
gap> A := [[3,2,0],[2,3,0],[0,0,5]];; gap> Eigenvalues(Rationals, A); [ 5, 1 ] gap> Eigenvectors(Rationals, A); [ [ 1, 1, 0 ], [ 0, 0, 1 ], [ 1, -1, 0 ] ]ただし、どの数体の範囲で求めるのかを指定する必要がある。 上の例では Rationals (有理数体) の範囲で求めている。 また固有値の重複は求められない。 固有値と固有ベクトルの順番も対応するとは限らない。 有理数体以外の体の指定については、あとで説明する。
多項式を扱うには、まず変数を用意しないといけない。 その際、どのような数体を係数とするのかも指定する。
gap> x := Indeterminate(Rationals, "x"); xこれで多項式を扱うための準備ができたことになる。 複数の変数が必要ならば、これを繰り返す。
gap> y := Indeterminate(Rationals, "y"); y gap> (x + y)^3; x^3+3*x^2*y+3*x*y^2+y^3多項式への値の代入、多項式の因数分解は以下の通りである。
gap> f := x^2 + x*y + y^2; y^2+y*x+x^2 gap> Value(f, [x], [2]); y^2+2*y+4 gap> Value(f, [x,y], [2,1]); 7 gap> Factors(x^2-1); [ x-1, x+1 ]多項式環は以下のように定義する。 x と y はすでに変数として用意されているものとする。
gap> R := PolynomialRing(Rationals, [x,y]); Rationals[x,y]
GAP では円分体 (cyclotomic field) に含まれる数以外はうまく扱えない。 例えば 2 の 3 乗根は円分体に含まれないので扱いにくい。 円周率などの超越数も扱えない。 GAP でよく用いられる数は
gap> ER(-2)^2; -2 gap> ER(-2); E(8)+E(8)^3有理数体 Rationals に 1 の n 乗根 E(n) を添加した体 (円分体) は CF(n) で表される。
gap> M := [[0,1],[2,0]]; [ [ 0, 1 ], [ 2, 0 ] ] gap> Eigenvalues(Rationals, M); [ ] gap> Eigenvalues(CF(8), M); [ E(8)-E(8)^3, -E(8)+E(8)^3 ]平方根 E(a) を扱うために CF(n) として n をいくつにすればよいかは、 それほど簡単ではない。 これを知るためには
gap> DefaultField(ER(3)); CF(12)などとすればよい。
q 個の元からなる有限体 (finite field, Galois field) は GF(q) と表される。 GF(q) の乗法群は巡回群となるので、その生成元の一つを Z(q) で表す。 GF(q) の 0 でない元は Z(q)^i と表すことができるので、GAP はこの形で表示する。 また 0 は有理数としての 0 特別するため 0*Z(q) と表される。 GF(q) を有理整数環上の加群と見ることができるので、GF(q) の元に整数をかけることは許される。 また q が q' の約数であるとき、自然に GF(q) は GF(q') の部分体とみなされる。
gap> Z(4)+Z(4)^2; Z(2)^0 gap> Z(4)-Z(4); 0*Z(2) gap> 3*Z(4); Z(2^2) gap> Z(4) in GF(16); true有限体の元を成分とする行列なども問題なく扱うことが出来る。
gap> M := [[1,1],[1,0]] * Z(4)^0; [ [ Z(2)^0, Z(2)^0 ], [ Z(2)^0, 0*Z(2) ] ] gap> Eigenvalues(GF(2), M); [ ] gap> Eigenvalues(GF(4), M); [ Z(2^2), Z(2^2)^2 ]
GAP はそれ自身で多くの機能をもっているが、もちろん何でも出来るわけではない。 そこで多くの人が GAP 上で特定の用途に用いるためのプログラムを作成し、パッケージとして公開している。 パッケージを利用することによって GAP の利用できる範囲は大きく膨らむ。 しかしながら、今回利用している Knoppix/Math の環境では DVD から起動しているため、 新たなパッケージをインストールこることはできない。 パッケージを利用したければ Knoppix をハードディスクにインストールしたり、 あるいは UBUNTU などの Linux ディストリビューションに GAP をインストールするなどする必要がある。 GAP は MS Windows にもインストールすることができるが、いくつかのパッケージは Windows 上では利用できない。
Akihide Hanaki (Shinshu University)