【发布时间】:2016-01-07 07:19:52
【问题描述】:
我决定学习模拟退火作为攻击this problem 的新方法。它本质上询问如何用 -1、0 或 1 填充网格,以便每一行和每一列的总和都是唯一的。作为一个测试用例,我使用了一个 6x6 的网格,Neil 给出的肯定有一个最优解:
1 1 1 1 1 1 6
1 1 1 1 1 -1 4
1 1 1 1 -1 -1 2
1 1 0 -1 -1 -1 -1
1 0 -1 -1 -1 -1 -3
0 -1 -1 -1 -1 -1 -5
5 3 1 0 -2 -4
我的代码在大多数运行时通常不会达到最佳情况,甚至返回错误的网格成本(old_cost 应该匹配 count_conflict(grid))。是我的参数设置不正确,是我执行不正确,还是模拟退火在这里不是一种可行的方法?
import random
from math import exp
G_SIZE = 6
grid = [[1]*G_SIZE for i in range(G_SIZE)]
def count_conflict(grid):
cnt = [0]*(2*G_SIZE+1)
conflicts = 0
for row in grid:
cnt[sum(row)] += 1
for col in zip(*grid):
cnt[sum(col)] += 1
#print(cnt)
for c in cnt:
if c == 0: conflicts += 1
if c > 1: conflicts += c-1
return conflicts
def neighbor(grid):
new_grid = grid[:]
i = random.choice(range(G_SIZE))
j = random.choice(range(G_SIZE))
new_cells = [-1, 0, 1]
new_cells.remove(new_grid[i][j])
new_grid[i][j] = random.choice(new_cells)
return new_grid
def acceptance_probability(old_cost, new_cost, T):
if new_cost < old_cost: return 1.0
return exp(-(new_cost - old_cost) / T)
# Initial guess
for i in range(1, G_SIZE):
for j in range(0, i):
grid[i][j] = -1
#print(grid)
old_cost = count_conflict(grid)
T = 10.0
T_min = 0.1
alpha = 0.99
while T > T_min:
for i in range(1000):
new_sol = neighbor(grid)
new_cost = count_conflict(new_sol)
ap = acceptance_probability(old_cost, new_cost, T)
print(old_cost, new_cost, ap, T)
if ap > random.random():
grid = new_sol
old_cost = new_cost
T *= alpha
for row in grid:
print(row)
print(count_conflict(grid))
【问题讨论】:
-
不知何故,我认为基本问题应该张贴在数学问题板上,因为您似乎可以将此问题转换为一组具有多个变量的链接方程,从而转换为矩阵计算。
-
我很确定 SA 不提供任何找到全局最小值的保证。你掷骰子的次数越多,在某些情况下,你就越有可能找到它。使用您的冷却配置文件。这是你必须半手动完成的事情,至少一开始是这样。您能否将您的例程与已知事物进行比较 - 为什么不与
scipy.optimize.anneal并行运行并比较行为。 -
@uhoh 我已经修改了一些参数,但程序通常在未达到预期目标时停止。由于模拟退火适用于 N 皇后问题,我希望它可以在这里工作。
-
小心你所说的“作品”。您必须仔细查看它的运行方式、起始案例的数量、温度等。SA 可以工作和不工作,具体取决于细节。如果您改用 3D NumPy 数组,您也可以尝试并行运行 100 个案例。只是。
-
一般来说,不存在可以保证找到全局最优值的算法。
标签: python constraints heuristics simulated-annealing