模拟退火不是 return (an) 最优解
Simulated annealing doesn't return (an) optimal solution
我决定学习模拟退火作为一种新的攻击方法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
我的代码通常不会在大多数运行中达到最佳情况,甚至 returns 错误的网格成本(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))
首先要做的几件事可能会很快引导您找到可行的解决方案,而无需执行任何其他操作(例如,交换启发式):
- 在主迭代循环的顶部附近添加一行,以
计算您的 t0 状态的成本(即,您的起始
配置);
在主循环中,在
计算当前迭代成本的行——写
到文件,成本函数返回的值
迭代;在其下方添加一行,每 20 次打印该值
迭代或类似的东西(例如,大约每秒一次
大约和我们理解滚动数据一样快)
if n % 10 == 0: 打印(what_cost_fn_returned_this_iteration)
不要打电话acceptance_probability;没有自然收敛
组合优化问题中的准则;通常的做法
是在发生这些情况时跳出主循环:
已达到最大迭代次数
成本函数的当前最小值
过去 __ 次迭代的变化小于 __%;例如
如果在最后 100 次迭代中,成本(通过比较
使用移动的最小值和最大值 window) 变化小于 1%
在迭代过程中达到最小值后,成本现在为
随着迭代次数持续增加
其他一些观察结果:
有了适当的诊断(见上文),您将能够
确定:(i) 从一些初始成本来看,我的求解器在做什么? IE,
它是否在 more-or-less 直接路径中移动到 lower-and-lower 值?
它在振荡吗?它在增加吗? (如果是后者,修复方法是
通常你的标志是倒过来的)
一个 6 x 6 的矩阵非常非常小——留给
的空间不多了
使用成本函数
re-write 你的成本函数使得 "perfect" 解 returns a
零成本,其他的都有更高的价值
1000 次迭代并不多;尝试将其增加到 50,000
new_grid = grid[:]
进行浅拷贝。深层复制或就地修改网格并恢复到原始状态可以解决问题。
我决定学习模拟退火作为一种新的攻击方法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
我的代码通常不会在大多数运行中达到最佳情况,甚至 returns 错误的网格成本(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))
首先要做的几件事可能会很快引导您找到可行的解决方案,而无需执行任何其他操作(例如,交换启发式):
- 在主迭代循环的顶部附近添加一行,以 计算您的 t0 状态的成本(即,您的起始 配置);
在主循环中,在 计算当前迭代成本的行——写 到文件,成本函数返回的值 迭代;在其下方添加一行,每 20 次打印该值 迭代或类似的东西(例如,大约每秒一次 大约和我们理解滚动数据一样快)
if n % 10 == 0: 打印(what_cost_fn_returned_this_iteration)
不要打电话acceptance_probability;没有自然收敛 组合优化问题中的准则;通常的做法 是在发生这些情况时跳出主循环:
已达到最大迭代次数
成本函数的当前最小值 过去 __ 次迭代的变化小于 __%;例如 如果在最后 100 次迭代中,成本(通过比较 使用移动的最小值和最大值 window) 变化小于 1%
在迭代过程中达到最小值后,成本现在为 随着迭代次数持续增加
其他一些观察结果:
有了适当的诊断(见上文),您将能够 确定:(i) 从一些初始成本来看,我的求解器在做什么? IE, 它是否在 more-or-less 直接路径中移动到 lower-and-lower 值? 它在振荡吗?它在增加吗? (如果是后者,修复方法是 通常你的标志是倒过来的)
一个 6 x 6 的矩阵非常非常小——留给
的空间不多了 使用成本函数re-write 你的成本函数使得 "perfect" 解 returns a
零成本,其他的都有更高的价值1000 次迭代并不多;尝试将其增加到 50,000
new_grid = grid[:]
进行浅拷贝。深层复制或就地修改网格并恢复到原始状态可以解决问题。