The Negligible Lab

Astray in the forest of electrical and electronic circuits. Adrift in the gap between time and frequency domains. 独立独歩を是とする。

1 ~ 25を1回ずつ用いた5 × 5行列の最大の行列式

はじめに

久しぶりの投稿として,数ヶ月前にやってみた数学の遊びについて書いてみます。

X(旧Twitter)で,5 × 5の行列を眺めていたら,「ビンゴみたい」と言われた──というポストを見かけました。なるほど25個の数が正方形の形に並んでいたらビンゴに見えますね。ならば,1 ~ 25の自然数がそれぞれ1回ずつ使われた5 × 5行列を「ビンゴ行列」と呼ぶこととし,ランダムに配られたビンゴ行列の──そうですね,行列式が最も大きいものが勝ち,としてみましょう。さて,このとき,最強のビンゴ行列,つまり理論上可能な最大の行列式はいくつでしょうか?

1 ~ 25の自然数をそれぞれ1回ずつ使った5 × 5行列をビンゴ行列と呼ぶとき,ビンゴ行列として可能な最大の行列式はいくつか? また,そのビンゴ行列は何か?

例えば綺麗に数字が並んだ場合,

\begin{vmatrix}
1 & 2 & 3 & 4 & 5 \\
6 & 7 & 8 & 9 & 10 \\
11 & 12 & 13 & 14 & 15 \\
16 & 17 & 18 & 19 & 20 \\
21 & 22 & 23 & 24 & 25
\end{vmatrix}
= 0 \tag{1}

となります。一方,1と2,16と25をそれぞれ入れ替えた場合,

\begin{vmatrix}
2 & 1 & 3 & 4 & 5 \\
6 & 7 & 8 & 9 & 10 \\
11 & 12 & 13 & 14 & 15 \\
25 & 17 & 18 & 19 & 20 \\
21 & 22 & 23 & 24 & 16
\end{vmatrix}
= 405 \tag{2}

となります。数の並び方を工夫すれば,もっと大きな行列式が作れるでしょう。しかし,最大値は何でしょうか? これは組み合わせ最適化問題と言えそうですね。今回はこれを考えてみようと思います。

AIに頼る

膨大な計算時間

単純計算でビンゴ行列は25! = 15,511,210,043,330,985,984,000,000個あります。すべてのケースの行列式を計算するとして,1つあたり1 μsで計算できたとしても,15,511,210,043,330,985,984 s = 491,857,243,890年 ≃ 4,920億年を要します。行列式は転置しても変わらず,行と行を入れ替えても,列と列を入れ替えても,符号が変わるのみで絶対値は変わりません。これらの性質を利用すれば,計算の回数は減らせるはずですが,膨大な時間を要するという点は変わらないように思えます。

ChatGPTに聞いてみる

どうしたものか…せや!こんなときこそAIだ! ChatGPTに質問してみました。図1にその様子を貼り付けます。

図1: ChatGPT 4oからの回答
ChatGPT 4o*1はシミュレーテッドアニーリングを提案しました。さらにこの直後にPythonのコードも示してくれました。

import numpy as np
import random
import math

def initial_matrix():
    numbers = list(range(1, 26))
    random.shuffle(numbers)
    return np.array(numbers).reshape(5, 5)

def calculate_score(matrix):
    return abs(np.linalg.det(matrix))

def get_neighbor(matrix):
    new_matrix = matrix.copy()
    i1, i2 = random.sample(range(25), 2)
    x1, y1 = divmod(i1, 5)
    x2, y2 = divmod(i2, 5)
    new_matrix[x1][y1], new_matrix[x2][y2] = new_matrix[x2][y2], new_matrix[x1][y1]
    return new_matrix

def simulated_annealing(iterations=100000, initial_temp=1000, cooling_rate=0.003):
    current = initial_matrix()
    current_score = calculate_score(current)
    best = current
    best_score = current_score
    temp = initial_temp

    for step in range(iterations):
        neighbor = get_neighbor(current)
        neighbor_score = calculate_score(neighbor)
        delta = neighbor_score - current_score

        if delta > 0 or random.random() < math.exp(delta / temp):
            current = neighbor
            current_score = neighbor_score
            if current_score > best_score:
                best = current
                best_score = current_score

        temp *= (1 - cooling_rate)

    return best, best_score

# 実行例
if __name__ == "__main__":
    best_matrix, best_det = simulated_annealing()
    print("最大行列式:", best_det)
    print("対応する行列:\n", best_matrix)

実行しますかと問われたので,実行してくださいとお願いしました。すると,下図のような結果が得られました。

図2: ChatGPT 4oによるシミュレーテッドアニーリングの実行結果
書き出せば,

\begin{vmatrix}
6 & 18 & 21 &  4 & 17 \\
15 & 1 & 10 & 13 & 24 \\
19 & 7 & 22 & 14 & 2 \\
5 & 16 &  9 & 25 & 11 \\
23 & 20 & 3 & 8 &12
\end{vmatrix}
= 6,793,693 \tag{3}

という答えを叩き出しました! 680万に迫るか!

ローカルでも試す

ChatGPTの提示したPythonコードをローカルのJupyterLabでも走らせてみました。筆者は最近,UMPCを1台新たに導入しました。Zwide NA08Hです。図3のように若干重いながらもBlenderを動かすこともできます。以前使用していた愛機ASUS*2 TransBook T90 Chiの代わりですね。

図3: Zwide NA08HでBlender 4.4を動かしている様子
このNA08HでJupyterLabを動かし,先ほどのPythonコードをペーストして走らせてみると…

\begin{vmatrix}
9 & 19 & 2 & 22 & 14 \\
7 & 6 & 16 & 10 & 25 \\
5 & 17 & 24 & 15 & 4 \\
23 & 3 & 13 & 18 & 8 \\
20 & 21 & 11 & 1 & 12
\end{vmatrix}
= 6,831,765 \tag{4}

ランダムで生成している初期行列が異なるためか,このような結果が得られます。683万まで来ました!

まとめ

その後もパラメータを変えて試行錯誤してみましたが,680万前後となって劇的に変わることはありませんでした。どうやらビンゴ行列の行列式の最大値は680万あたりであろうことは分かりました。真の最大値は分かるのでしょうか? 別のアルゴリズムで探してみるなど他にもアプローチがあるかもしれません。

今回は,思いついた数学の問題に対してAIの力を借りるという試行でした。今後,制御工学やパワーエレクトロニクスの勉強・野良研究を続けていく上でAIが味方になってくれるのであれば大変心強いですね。

*1:技術の流れの速さを感じて課金しました。

*2:アスース派です。