代码之家  ›  专栏  ›  技术社区  ›  qwr

模拟退火无法返回最优解

  •  0
  • qwr  · 技术社区  · 9 年前

    我决定学习模拟退火作为一种新的攻击方法 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))
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   doug    9 年前

    首先要做的几件事,这可能会很快让你找到一个有效的解决方案,而无需做其他事情(例如,交换试探法):

    • 在主迭代循环的顶部附近添加一行 计算t0状态的成本 (即,你的出发点 配置);
    • 在主循环中,在 计算当前迭代成本的行 将成本函数返回的值添加到文件 迭代;在下面添加一行,每隔20打印一次该值 迭代或类似的事情(例如,大约每秒一次 大约与我们理解滚动数据的速度一样快)

      如果n%10==0:打印(what_cost_fn_returned_this_iteration)

    • 不要调用acceptance_probability ; 没有自然收敛 组合优化问题中的准则;通常的做法 当发生以下任何情况时,将脱离主循环:

      已达到最大迭代计数

      成本函数的当前最小值 过去__次迭代的变化小于__%;例如 如果在过去100次迭代中 使用移动窗口的最小值和最大值)变化小于1%

      在迭代过程中达到最小值后,成本现在为 随着迭代次数不断增加


    其他一些观察 :

    • 有了诊断(见上文),您将能够 确定:(i)根据一些初始成本,我的求解器在做什么?即, 它是在或多或少地直接走向越来越低的价值? 它在振荡吗?它在增加吗?(如果是后者,则修复为 通常你有一个向后的标志)

    • 6x6矩阵非常小,这对
      要使用的成本函数

    • 重新编写成本函数,以便“完美”解决方案返回
      零成本,所有其他都有更高的价值

    • 1000次迭代并不多;试着增加到50000

        2
  •  0
  •   qwr    9 年前

    new_grid = grid[:] 制作一个浅薄的副本。深度复制或就地修改栅格并恢复到原始栅格可以解决此问题。