🌀

Juliaで有限体を扱ってみたいなら...

2024/06/22に公開

Juliaで森博嗣さんのビリヤード問題な完全差集合の問題を解きたい

そのためには先ず, 有限体をJuliaで扱えるようになる必要がある。
とはいえ, Nemo.jpというパッケージがあるので先ずはそのパッケージでの有限体の扱いに慣れるところから。

Nemo.jpを用いる

JuliaのインストールやJuliaを用いる環境としてのJupyter Notebookの導入などは様々にwebに指南があるのでここでは詳細は省く。
この記事を書いてる環境は2024.6.22.現在で
マシンはMacBookPro(2023.11.M3,16GB), OSはMacOSX14.5なSonoma.
JuliaのVERSIONは1.10.4, Nemo は, 0.45.5

using Nemo

Packageが導入できていれば, Jupyter Notebookでは次のように表示されます。

Welcome to Nemo version building
Nemo comes with absolutely no warranty whatsoever

では実際に有限体を使ってみましょう。

Nemoで有限体を設定する

有限体とは何か?

「位数が有限である体を有限体(またはガロア体)と言います。」
「大雑把に言うと,四則演算ができる有限集合のことです。」
と某「高校数学の美しい物語」には書いてあります。まぁ慣れていない人にはそれで十分かも。
具体的には例えばGF(2)で位数2のガロア体を表すとして,
GF(2)の要素の数は位数と表現されそれが2なので通常は\{0,1\}とします。
Nemoでは

F2 = GF(2)

のようにして有限(ガロア)体F2を指定します。
すると,

Prime field of characteristic 2

のように表示され, F2を用いる準備ができます。
ここでF2を表示させようとして,

F2

としても先ほどと同じ

Prime field of characteristic 2

という表示しか返ってきません。
そこで例えば,

for a ∈ F2
    print(a,", ")
end

とすると,

0,1,

のように, 想定していた通りのF2の要素というか元を表示して返してくれます。
尚, 「∈」は「集合に元が含まれる」という記号ですが, jupyter notebookの場合は,
「\in」と打ち込んでからtabキーを押すと「∈」に変えて使えます(有り難く便利です)。
a in F2でも同様(というか本来はこちら?)にできますが, 見易いので「∈」を用いています。

有限体の拡大体も

ビリヤード問題を解くには, 拡大体が必要になります。

F4 = GF(2,2)

で, 位数 2^2=4 の拡大体が設定できます。

Finite field of degree 2 and characteristic 2

のように表示されます。(拡大)次数 2 とありますね。
有限体は位数が素数の冪乗であれば存在することが分かっています。
Wikipediaには

\mathbf{F}_2 係数一変数多項式環 \mathbf{F}_2[x] を考える。
その既約多項式 f(x) = x^2 + x + 1 の生成する素イデアル(f(x)) は、 \mathbf{F}_2[x] がPIDなので、特に極大イデアル。したがって剰余環 \mathbf{F}_4 = \mathbf{F}_2[x]/(f(x)) は 4 個の元からなる体である。
変数 x の自然な全射による像を ω とおくと、\mathbf{F}_4 = \{0,\ 1,\ ω,\ ω^2\} と表せ、その演算は関係式 ω^2 + ω + 1 = 0 から定まる。

とあり, 「素イデアル」や「自然な全射」など初心者には分かり難い表記はあるものの, 既約多項式によって拡大される事は理解できるかも。

ここで先ほどと同様にF4の中身を見たいとなると,

for a ∈ F4
    print(a,", ")
end

とすれば良いわけですが, 実際に打ち込んでみると

0, 1, o, o + 1,

と返ってきます。oって何?って感じですが, これはWikipediaの解説にあるωです。
いやいや普通 \omega とか \alpha やろって思うたあなたは,

F4 = GF(2,2,"ω")

とすれば, ωを使ってくれます。何というか文字を指定できるわけです。
この「ω」も「∈」同様で「\omega」と打ち込んでからtabキーを押すと「ω」に変わって使えます。
こうしておいて,

for a ∈ F4
    print(a,", ")
end

とすれば,

0, 1, ω, ω + 1,

と返してくれるわけです。

では, 既約多項式について確認してみましょう

そもそも既約多項式とは何でしょう。
既約多項式とは「それ以上因数分解できない多項式」といえば良いでしょうか。
例えば, GF(2)を係数とする多項式環で1次の既約多項式は, x,\ x+1 の2つです。
では2次だとどうでしょうか。
「GF(2)を係数とする多項式環」なので係数は \{0,\ 1\} しかありません。
だから, 2次の多項式は x^2,\ x^2+1,\ x^2+x,\ x^2+x+1 の4つだけです。
此の内 x^2=x\times x ですし, x^2+x=x(x+1) ですから, 此の2つは, 既約ではなく「合成」多項式です。
残るは x^2+1x^2+x+1 です。
此の内, x^2+1 は普通なら因数分解はちょっと... となりますが,
係数がGF(2)なので, (x+1)^2=x^2+2x+1=x^2+1(1+1=2=0だから)と合成多項式となります。
最後の x^2+x+1 だけが既約多項式となるのですが... どうすれば既約と分かるでしょうか...
色々と手法はあるようですが, 最も単純なのは, 合成できる多項式を列挙して確認する方法です。
つまり,

\times x x+1
x x^2 x^2+x
x+1 x^2+x x^2+2x+1=x^2+1

のように, 合成できる多項式を全部確認するという手法で, 此の表に現れない x^2+x+1 は既約多項式だと確認できるわけです。

ちなみに

defining_polynomial(F4)

とすると, F4の生成多項式(既約多項式の一つ)が表示されますが,

x^2 + x + 1

と返ってきます。

3次の既約多項式についてはどうしましょうか

先程と同様に, 3次の合成できる多項式を確認すれば良いでしょう。

\times x^2 x^2+1 x^2+x x^2+x+1
x x^3 x^3+x x^3+x^2 x^3+x^2+x
x+1 x^3+x^2 x^3+x^2+x+1 x^3+x x^3+1

そして, 「GF(2)を係数とする3次の多項式」は x^3, x^3+1, x^3+x, x^3+x+1, x^3+x^2, x^3+x^2+1, x^3+x^2+x, x^3+x^2+x+1 の8つであり, 此の内先ほどの表にある合成多項式でないものは...
(...もう此の程度で見て確認するのは困難かも...)
x^3+x+1,\ x^3+x^2+1 の2つですね。

結局どうすれば既約多項式が確認できるか...

結局, n次までの多項式(合成多項式と既約多項式の両方)が確認できれば, n-k次とk+1次の多項式の積を確認することで, n+1次の合成多項式がチェックできて, それらをn+1次の多項式から除けば, n+1次の既約多項式が確認できる, という素朴な手法が考えられます。

コレをJuliaで実装してみましょう。


この実装の前に少し確認をしておきましょう。
例えば,

F8=GF(2,3,"ω")

として, 2^3の拡大体を設えると,

Finite field of degree 3 and characteristic 2

となり,

defining_polynomial(F8)

に対しては,

x^3 + x + 1

と返ってきて,

for a ∈ F8
    print(a,", ")
end

に対しては,

0, 1, ω, ω + 1, ω^2, ω^2 + 1, ω^2 + ω, ω^2 + ω + 1,

となります。
生成多項式である既約多項式は3次になるわけです。
2^4の拡大体や3^3の拡大体ではどのようになるでしょう。想定できますか?


ではやってみたコードを。
先ずは, n次の多項式を列挙するために,

all_perm(x, n) = vec(map(collect, Iterators.product(ntuple(_ -> x, n)...)))

という重複順列を列挙するall_perm(配列,項数)というfunctionを拵えます。
その上で,

p,n=2,2
Fp = GF(p,n,"α")
S, x = Fp["x"]
println(Fp,",  ",S)
Fpl=[]
for a in Fp
    push!(Fpl,a)
end
println(Fpl)

のようにして, 有限体を配列Fplにセットして, その配列を係数とした多項式を列挙していきます。

alst=[]
m=3
for i=1:m-1
    push!(alst,[])
end
for i=1:round(Int8,m/2)
    for a ∈ all_perm(Fpl, i)
        f=x^i
        for j=1:i
            f=f+a[j]*x^(j-1)
        end
        push!(alst[i],f)
    end
end
l=length(alst)
blst=[]
for i=1:m-l
    for a ∈ alst[i],b ∈ alst[l-i+1]
        push!(blst,a*b)
    end
end
unique!(blst)
clst=[]
for a ∈ all_perm(Fpl, m)
    f=x^m
    for j=1:m
        f=f+a[j]*x^(j-1)
    end
    push!(clst,f)
end
dlst=[]
for c ∈ clst
    if !(c ∈ blst)
        push!(dlst,c)
    end
end
println(dlst)

Any[x^3 + α, x^3 + α + 1, x^3 + x + 1, x^3 + αx + 1, x^3 + (α + 1)x + 1, x^3 + x^2 + 1, x^3 + x^2 + x + α, x^3 + x^2 + x + α + 1, x^3 + x^2 + αx + α + 1, x^3 + x^2 + (α + 1)x + α, x^3 + αx^2 + 1, x^3 + αx^2 + x + α + 1, x^3 + αx^2 + αx + α, x^3 + α*x^2 + (α + 1)x + α, x^3 + αx^2 + (α + 1)*x + α + 1, x^3 + (α + 1)*x^2 + 1, x^3 + (α + 1)*x^2 + x + α, x^3 + (α + 1)x^2 + αx + α, x^3 + (α + 1)x^2 + αx + α + 1, x^3 + (α + 1)*x^2 + (α + 1)*x + α + 1]

p=2,\ n=2 つまり, GF(2^2)m=3 とした3次の場合だと, 上のような既約多項式の配列が得られます。

Discussion