はじめに
GeminiとChatGPTの2つのAIに少し意地悪な最適化問題を解かせ、その実力を検証しました。
数理モデルで得られる「最適解」に対して、AIがどれだけ近い解を見つけられるかを見ていきます。
問題:近似値クイズ(数字の重複禁止)
AIにDeepResearchで解いてもらった問題は、以下の通りです。
下記の4つの問題の「#」に数字を埋めてください。
1. IPv4のビット数 = ##
2. ピアノの鍵盤の数 = ##
3. 北岳の標高 = 3###
4. 周期表の元素数 = ###
**ルール**
* 「#」は0から9のどれかを入れること
* 「#」に同じ数字を使わないこと
* 問題の解答と数字の差の和を最小化すること
正解とルールを満たす解答
本来の正解は次のとおりです。
- IPv4のビット数 = 32
- ピアノの鍵盤数 = 88
- 北岳の標高(m) = (3)193
- 元素の数 = 118
このままでは数字の「1」や「8」が重複しているため、ルールを満たせません。そこで、ルールを守りつつ、誤差の合計が最も小さくなる組み合わせを探すのがこの問題のゴールです。
数理最適化ソルバーで厳密解を求めたところ、最小誤差は44
であることがわかりました。この最小誤差を達成する解の組み合わせは12通りあり、その一例がこちらです。
問題 | 解答例1 | 解答例2 |
---|---|---|
IPv4のビット数 | 32 | 32 |
ピアノの鍵盤数 | 86 | 74 |
北岳の標高 | (3)174 | (3)186 |
周期表の元素数 | 95 | 95 |
誤差合計 | 44 | 44 |
※ 周期表の元素数(3桁)の解答が95
のような2桁になる場合は、095
と解釈します。
Gemini 2.5 Proの解答
Geminiは、やや決め打ちのような推論で、以下の解を導き出しました。
-
解答
- IPv4のビット数:
67
- ピアノの鍵盤数:
89
- 北岳の標高:
(3)204
- 周期表の元素数:
135
- IPv4のビット数:
-
使用数字:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
(重複なし) -
誤差の合計:
64
- $|67-32| + |89-88| + |3204-3193| + |135-118| = 35 + 1 + 11 + 17 = 64$
最適解には及びませんでしたが、複雑な制約を理解して解を生成しました。
ChatGPTの解答
一方、ChatGPTは思考過程を示さなかったものの、次の解答を提示しました。
-
解答
- IPv4のビット数:
35
- ピアノの鍵盤数:
89
- 北岳の標高:
(3)206
- 周期表の元素数:
147
- IPv4のビット数:
-
使用数字:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
(重複なし) -
誤差の合計:
46
- $|35-32| + |89-88| + |3206-3193| + |147-118| = 3 + 1 + 13 + 29 = 46$
誤差46
は、最適解(44
)にかなり近い値です。
まとめ(総評)
今回の検証結果をまとめます。
最適解 | ChatGPT | Gemini | |
---|---|---|---|
誤差合計 | 44 | 46 | 64 |
両モデルとも、まずは各問題の本来の正解(32, 88, 3193, 118
)を把握した上で、数字を重複させないという制約を満たすために、各数値を調整するアプローチをとっているようでした。
最適解を見つけることはできませんでしたが、ChatGPTの結果は最適解に近いです。これが偶然か工夫された推論かは不明ですが、このようなちょっと難しい最適化問題でも「それなりに良い解」が得られるのは興味深いです。
付録(数理最適化による解法)
以下のPythonコードを使えば、数理最適化で最小誤差の解を求められます。
import numpy as np
from mip import Model, minimize, xsum
m = Model()
nd = [2, 2, 3, 3] # 桁数
ans = [32, 88, 193, 118] # 正解
# 問題ごと・桁ごと・(1〜9の)数字ごとの0-1変数
xs = [
m.add_var_tensor((nd[i], 9), f"x{i}", var_type="B")
for i in range(4)
]
# 問題ごとの数字
ys = np.array([
sum(xsum(xs[i][j] * range(1, 10)) * 10**j for j in range(nd[i]))
for i in range(4)
])
# 問題ごとの誤差
zs = m.add_var_tensor((4,), "z")
# 全問題の桁ごと数字ごとの0-1変数
vs = np.vstack(xs)
m.objective = minimize(xsum(zs)) # 目的関数
for vx in vs:
m += xsum(vx) <= 1 # 各桁は1つの数字をとる
for vw in vs.T:
m += xsum(vw) == 1 # 各数字は一度しか使えない
for i in range(4):
m += zs[i] >= ys[i] - ans[i]
m += zs[i] >= ans[i] - ys[i]
m.optimize()
print(m.status, m.objective_value)
print(ys.astype(float))
結果
OptimizationStatus.OPTIMAL 44.0
[ 32. 86. 174. 95.]