Nがスタックの数、Pがスタックの容量であるNXPスタックのセットが与えられた場合、ロケーションAのノードから任意のロケーションBに移動するために必要なスワップの最小数を計算するにはどうすればよいですか?私はゲームを設計しています。最終的な目標は、すべてのスタックをすべて同じ色になるように並べ替えることです。
# Let "-" represent blank spaces, and assume the stacks are
stacks = [
['R', 'R', 'R', 'R'],
['Y', 'Y', 'Y', 'Y'],
['G', 'G', 'G', 'G'],
['-', '-', '-', 'B'],
['-', 'B', 'B', 'B']
]
stacks[1][1]
そんな時に「B」を挿入したい場合stacks[1] = ["-", "B", "Y", "Y"]
。そのために必要な移動の最小数を決定するにはどうすればよいですか?
私は複数のアプローチを見てきました。ある状態から可能なすべての動きを生成し、それらにスコアを付け、次に最良のスコアリングパスを継続する遺伝的アルゴリズムを試しました。また、問題をパスファインディングするためにDjikstraのアルゴリズムを実行しようとしました。イライラするほどシンプルに見えますが、指数時間以外で実行する方法を理解できません。ここで適用できる欠落しているアルゴリズムはありますか?
編集する
必要な移動の最小数を計算するためにこの関数を書きました:スタック:スタック内のピースを表すキャラクターのリストのリスト、スタック[0] [0]はスタックのトップです[0]スタック_インデックス:インデックスピースを追加するスタックneeds_piece:スタックに追加するピースneeds_index:ピースを配置するインデックス
def calculate_min_moves(stacks, stack_ind, needs_piece, needs_index):
# Minimum moves needed to empty the stack that will receive the piece so that it can hold the piece
num_removals = 0
for s in stacks[stack_ind][:needs_index+1]:
if item != "-":
num_removals += 1
min_to_unlock = 1000
unlock_from = -1
for i, stack in enumerate(stacks):
if i != stack_ind:
for k, piece in enumerate(stack):
if piece == needs_piece:
if k < min_to_unlock:
min_to_unlock = k
unlock_from = i
num_free_spaces = 0
free_space_map = {}
for i, stack in enumerate(stacks):
if i != stack_ind and i != unlock_from:
c = stack.count("-")
num_free_spaces += c
free_space_map[i] = c
if num_removals + min_to_unlock <= num_free_spaces:
print("No shuffling needed, there's enough free space to move all the extra nodes out of the way")
else:
# HERE
print("case 2, things need shuffled")
編集:スタックのテストケース:
stacks = [
['R', 'R', 'R', 'R'],
['Y', 'Y', 'Y', 'Y'],
['G', 'G', 'G', 'G'],
['-', '-', '-', 'B'],
['-', 'B', 'B', 'B']
]
Case 1: stacks[4][1] should be 'G'
Move 'B' from stacks[4][1] to stacks[3][2]
Move 'G' from stacks[2][0] to stacks[4][1]
num_removals = 0 # 'G' is directly accessible as the top of stack 2
min_to_unlock = 1 # stack 4 has 1 piece that needs removed
free_spaces = 3 # stack 3 has free spaces and no pieces need moved to or from it
moves = [[4, 3], [2, 4]]
min_moves = 2
# This is easy to calculate
Case 2: stacks[0][3] should be 'B'
Move 'B' from stacks[3][3] to stack[4][0]
Move 'R' from stacks[0][0] to stacks[3][3]
Move 'R' from stacks[0][1] to stacks[3][2]
Move 'R' from stacks[0][2] to stacks[3][1]
Move 'R' from stacks[0][3] to stacks[3][0]
Move 'B' from stacks[4][0] to stacks[0][3]
num_removals = 0 # 'B' is directly accessible
min_to_unlock = 4 # stack 0 has 4 pieces that need removed
free_spaces = 3 # If stack 3 and 4 were switched this would be 1
moves = [[3, 4], [0, 3], [0, 3], [0, 3], [0, 3], [4, 0]]
min_moves = 6
#This is hard to calculate
実際のコードの実装は難しい部分ではなく、私が苦労している問題を解決するアルゴリズムを実装する方法を決定しています。
@YonIifのリクエストに従って、問題の要点を作成しました。
実行すると、スタックのランダムな配列が生成され、ランダムな場所のランダムなスタックに挿入する必要があるランダムなピースが選択されます。
これを実行すると、この形式のものがコンソールに出力されます。
All Stacks: [['-', '-', 'O', 'Y'], ['-', 'P', 'P', 'O'], ['-', 'P', 'O', 'Y'], ['Y', 'Y', 'O', 'P']]
Stack 0 is currently ['-', '-', 'O', 'Y']
Stack 0 should be ['-', '-', '-', 'P']
状況更新
なんとかこの問題を解決していく決意です。
コメントで言及されている@Hans Olssonのように、ケースの数を最小限に抑える方法があることに注意してください。この問題に対する私の最近のアプローチは、前述のものと同様の一連のルールを開発し、それらを世代別アルゴリズムで使用することでした。
次のようなルール:
移動を取り消さないでください。1-> 0、次に0-> 1の順に移動します(意味がありません)
2回続けて駒を移動しないでください。0-> 1、1-> 3の順に移動しない
stacks [Z]が移動時と同じ状態にある場合、stacks [X]からstacks [Y]へのいくつかの移動、いくつかの移動、そしてstacks [Y]からstacks [Z]への移動が与えられます。 stacks [X]からstacks [Y]への移動が発生した場合、stacks [X]から直接stacks [Z]に移動することで移動を排除できた
現在、私は十分なルールを作成する試みでこの問題に取り組んでいます。これにより、「有効な」移動の数を最小限に抑え、世代別アルゴリズムを使用して回答を計算できるようになります。誰かが追加のルールを考えることができるなら、私はコメントでそれらを聞くのに興味があります。
更新
@RootTwoの回答のおかげで、ここで簡単に説明します。
突破口へ
ゴールの高さを、ゴールピースをデスティネーションスタックに配置する深さとして定義します。
いくつかのゴールピースがインデックス<= stack_height-ゴールの高さに配置されている場合は常に、clear_path()メソッドを介して、勝利への最短経路が常に存在します。
Let S represent some solid Piece.
IE
Stacks = [ [R, R, G], [G, G, R], [-, -, -] ]
Goal = Stacks[0][2] = R
Goal Height = 2.
Stack Height - Goal Height = 0
そのようないくつかのスタックが与えられるstack[0] = R
と、ゲームが勝ちます。
GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
それらは常に利用可能な少なくともstack_height空白スペースであることがわかっているため、考えられる最悪のケースは次のようになります。
[ [ S, S, !Goal ], [R, S, S], [-, -, -]
ゴールピースがゴールの目的地にないこと、またはゲームが勝利したことを知っているからです。その場合、必要な移動の最小数は移動です。
(0, 2), (0, 2), (0, 2), (1, 0)
Stacks = [ [R, G, G], [-, R, R], [-, -, G] ]
Goal = Stack[0][1] = R
Stack Height - Goal Height = 1
そのようないくつかのスタックが与えられるstack[1] = R
と、ゲームが勝ちます。
GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
使用可能な空白スペースが少なくとも3つあることがわかっているため、考えられる最悪のケースは次のとおりです。
[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
この場合、移動の最小数は移動になります。
(1, 2), (0, 2), (0, 2), (1, 0)
これはすべてのケースに当てはまります。
したがって、問題は、ゴールピースをゴール高さに配置するのに必要な最小数の動きを見つける問題にまで減少した。
これは問題を一連の副問題に分割します:
宛先スタックにアクセス可能なピースがある場合!=ゴールピース、そのピースの有効な場所があるかどうか、または別のピースが交換されている間、ピースがそこに留まるかどうかを決定します。
デスティネーションスタックにアクセス可能なピース==ゴールピースがある場合、それを削除して必要なゴールの高さに配置できるかどうか、または他のピースが交換されている間ピースを保持するかどうかを決定します。
上記の2つのケースで別のピースを交換する必要がある場合、ゴールピースがゴールの高さに到達できるようにするために、どのピースを交換するかを決定します。
宛先スタックは常にそのケースを最初に評価する必要があります。
IE
stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
ゴールスタックを確認すると、まず次のようになります。
(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
目標スタックを無視:
(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves