0

この問題に取り組むための新しい方法として、シミュレーテッド アニーリングを学ぶことにしました。基本的に、各行と列の合計が一意になるように、グリッドを -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))
4

2 に答える 2

1

最初にいくつかのことを行う必要があります。これにより、他に何もする必要なく (たとえば、ヒューリスティックを交換するなど)、すぐに実用的な解決策にたどり着くことができます。

  • t0 状態(つまり、開始構成)のコストを計算するために、メインの反復ループの外側の上部近くに行を追加し ます。
  • メイン ループ内で、現在の反復のコストを計算する行の直後に 1 つの print ステートメントを挿入します。これは、その反復のコスト関数によって返される値をファイルに書き込みます。そのすぐ下に、20回の繰り返しごとにその値を出力する行を追加します(たとえば、毎秒約1回は、スクロールデータを理解できる速さです)

    n % 10 == 0 の場合: print(what_cost_fn_returned_this_iteration)

  • accept_probability を呼び出さないでください。組み合わせ最適化問題には自然収束基準はありません。通常は、次のいずれかが発生したときにメイン ループから抜け出します。

    最大反復回数に達しました

    過去 __ 回の反復におけるコスト関数の現在の最小値の変化は __% 未満です。たとえば、最後の 100 回の反復で、(移動ウィンドウを使用して最小値と最大値を比較することにより) コストの変動が 1% 未満の場合

    反復中に最小値に達した後、反復回数に応じてコストが一貫して増加するようになりました


その他のいくつかの観察

  • 適切な診断 (上記参照) を使用すると、次のことを判断できます。(i) 初期コストから、私のソルバーは何をしているのか? つまり、値がどんどん下がっていきますか? 振動していますか?増えていますか?(後者の場合、修正は通常、標識を後方に持っていることです)

  • 6 x 6 のマトリックスは非常に小さいため、
    コスト関数を処理するためのスペースはあまりありません。


  • 「完全な」ソリューションがゼロコストを返し、他のすべてのソリューションがより高い値を持つように、コスト関数を書き直してください

  • 1000 回の反復は多くありません。それを50,000に増やしてみてください

于 2016-01-07T08:37:46.213 に答える