0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Lights OutをSagemathで解く(その3)

Posted at

前回の投稿(その2)ではLight Outゲーム(5 x 5)をSagemathを使って解きましたが。今回は一般的に(m x n)のLight Outが必ず解けるかを判定する方法を考えます。

(m x n)のLight Outが必ず解けるか

既に前々回でボタン・ライトの反転規則の行列を作る関数 makeA(m,x) を作ってありますので。この行列式が$0$でなければ必ず解けて、解は1つしかないことが分かります。

しかし5 x 5の場合のように行列式が$0$の時には、解けるかを判定したり、解の数を知るために直交基底ベクトルの数= 自由度(行の数ー階数) を求めた方が役に立ちます。

Sagemathでは階数は rank() プロパティを用いてば簡単に求まるので、$1 \le m, n \le 15$で求めてみます。

m, n = 15,15
print(np.array([[i*j-makeA(i, j).rank() for i in range(1,m+1)] for j in range(1,n+1)]))
# [[0 1 0 0 1 0 0 1 0 0 1 0 0 1 0]
# [1 0 2 0 1 0 2 0 1 0 2 0 1 0 2]
# [0 2 0 0 3 0 0 2 0 0 3 0 0 2 0]
# [0 0 0 4 0 0 0 0 4 0 0 0 0 4 0]
# [1 1 3 0 2 0 4 1 1 0 4 0 1 1 4]
# [0 0 0 0 0 0 0 6 0 0 0 0 0 0 0]
# [0 2 0 0 4 0 0 2 0 0 7 0 0 2 0]
# [1 0 2 0 1 6 2 0 1 0 2 0 7 0 2]
# [0 1 0 4 1 0 0 1 8 0 1 0 0 5 0]
# [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# [1 2 3 0 4 0 7 2 1 0 6 0 1 2 8]
# [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# [0 1 0 0 1 0 0 7 0 0 1 0 0 1 0]
# [1 0 2 4 1 0 2 0 5 0 2 0 1 4 2]
# [0 2 0 0 4 0 0 2 0 0 8 0 0 2 0]]

このアルゴリズムは$1 \le m, n \le 15$の範囲では問題ないのですが、これがもう少し大きくなると難しくなってきます。効率の良いアルゴリズムが「群論の味わい -置換群で解き明かすルービックキューブと15パズル-」の中にありましたので紹介します。

定理 6.5.3 パズルは$GCD(f_{M+1}(x)$,$f_{N+1}(x+1) )=1$の時にどんな初期状態からも解くことが出来る。ここで$f_{n}(x)$はフィボナッチ多項式

まずフィボナッチ多項式(Wikipedia)ですがこの説明にあるように。フィボナッチ数列を多項式にしたものということです。

F_n(x)= \left \{
\begin{array}{ll}
0, &if \ n = 0 \\
1, &if \ n = 1 \\
xF_{n-1}(x)+F_{n-2}(x) & if \ n \ge 2
\end{array}
\right.

これをSagemathでの実装すると以下のようになります。剰余環$\mathbb{Z}/2 \mathbb{Z}$ で演算する必要があるのでGF(2) で定義しています。

プログラムではn = 9までのフィボナッチ多項式を求めていますが、後のGCDの説明のためにfactor() を使って因数分解された形で表示しています。

R = PolynomialRing(GF(2),"x")
x = R.gen()
cache = dict()
def f(t,n):
    if n<2: return R(n)
    else:
        if (t,n) not in cache:
            cache[(t,n)] = f(t,n-2)+t*f(t,n-1)
        return cache[(t,n)]
    
for n in range(1,10):
    print(f"n={n}, f(x,{n})={f(x,n).factor()}")
# n=1, f(x,1)=1
# n=2, f(x,2)=x
# n=3, f(x,3)=(x + 1)^2
# n=4, f(x,4)=x^3
# n=5, f(x,5)=(x^2 + x + 1)^2
# n=6, f(x,6)=x * (x + 1)^4
# n=7, f(x,7)=(x^3 + x^2 + 1)^2
# n=8, f(x,8)=x^7
# n=9, f(x,9)=(x + 1)^2 * (x^3 + x + 1)^2

このフィボナッチ多項式を用いて5 x 5のLights Outが必ず解けるかをチェックしてみます。

f_{5+1}(x) = f_6(x) = x(x+1)^4 \\
f_{5+1}(x+1) = f_6(x+1) = (x+1)x^4 \\  
(\mathbb{Z}/2 \mathbb{Z}なのでx+2=xとなる)\\
したがって \\
GCD(f_6(x),f_6(x+1))=(x+1)x=x^2+x

となり1ではないので必ずは解けないことが分かります。さらにGCDの多項式の次数2が行列式の自由度 に等しくなります。

従ってm x nのLights Outの自由度 を求める関数F(m, n) は以下のように書けます。

def F(m,n):
    return f(x,m+1).gcd(f(x+1,n+1)).degree()
print(F1(5,5))
# 2

この関数F(m, n) を使うと $1 \le m, n \le 500$の範囲の自由度 を求めることが出来ました。

m = n = 500
print(np.array([[F(i,j) for i in range(1,m+1)] for j in range(1,n+1)]))
#[[ 0  1  0 ...  0  0  1]
# [ 1  0  2 ...  0  2  0]
# [ 0  2  0 ...  0  0  2]
# ...
# [ 0  0  0 ...  0  0  0]
# [ 0  2  0 ...  0 16  2]
# [ 1  0  2 ...  0  2  0]]

(開発環境:Cocalc Sage Worksheet)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?