Dato un set di stack NXP con N come numero di stack e P come capacità di stack, come posso calcolare il numero minimo di swap necessari per spostarmi da un nodo nella posizione A a una posizione arbitraria B? Sto progettando un gioco e l'obiettivo finale è quello di ordinare tutte le pile in modo che siano tutte dello stesso colore.
# 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']
]
Se voglio inserire una "B" in stacks[1][1]
tal senso stacks[1] = ["-", "B", "Y", "Y"]
. Come posso determinare il numero minimo di mosse necessarie per farlo?
Ho esaminato diversi approcci, ho provato algoritmi genetici che generano tutte le possibili mosse da uno stato, li segnano e quindi continuano lungo i migliori percorsi di punteggio, ho anche cercato di eseguire l'algoritmo di Djikstra per l'individuazione del percorso sul problema . Sembra frustrantemente semplice, ma non riesco a trovare un modo per farlo funzionare in qualcosa di diverso dal tempo esponenziale. C'è un algoritmo che mi manca applicabile qui?
modificare
Ho scritto questa funzione per calcolare il numero minimo di mosse richieste: pile: Elenco di Elenco dei personaggi che rappresentano i pezzi nella pila, pile [0] [0] è il primo della pila [0] stack_ind: l'indice del pila che il pezzo verrà aggiunto a needs_piece: il pezzo che dovrebbe essere aggiunto alla pila needs_index: l'indice in cui dovrebbe essere posizionato il pezzo
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")
Modifica: Casi di prova su pile:
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
L'implementazione del codice attuale non è la parte difficile, sta determinando come implementare un algoritmo che risolva il problema con cui sto lottando.
Come da richiesta di @ YonIif, ho creato una sintesi per il problema.
Quando viene eseguito, genera una matrice casuale delle pile e sceglie un pezzo casuale che deve essere inserito in una pila casuale in una posizione casuale.
Eseguendolo stampa qualcosa di questo formato sulla console.
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']
Aggiornamento di stato
Sono molto determinato a risolvere questo problema in qualche modo .
Tieni presente che ci sono modi per ridurre al minimo il numero di casi, come quelli di @Hans Olsson citati nei commenti. Il mio approccio più recente a questo problema è stato quello di sviluppare un insieme di regole simili a quelle menzionate e di impiegarle in un algoritmo generazionale.
Regole come:
Non invertire mai una mossa. Vai da 1> 0 quindi 0-> 1 (non ha senso)
Non spostare mai un pezzo due volte di seguito. Non passare mai da 0 -> 1 quindi 1 -> 3
Dato uno spostamento dalle pile [X] alle pile [Y], quindi un certo numero di mosse, quindi un passaggio dalle pile [Y] alle pile [Z], se le pile [Z] si trovano nello stesso stato in cui si trovava quando la mossa dalle pile [X] alle pile [Y], una mossa avrebbe potuto essere eliminata spostandosi dalle pile [X] direttamente alle pile [Z]
Attualmente, sto affrontando questo problema con un tentativo di creare abbastanza regole, che minimizzi il numero di mosse "valide", abbastanza da poter calcolare una risposta usando un algoritmo generazionale. Se qualcuno può pensare a regole aggiuntive, sarei interessato a sentirle nei commenti.
Aggiornare
Grazie alla risposta di @RootTwo ho avuto una svolta, che tratterò qui.
Sulla svolta
Definire l'altezza della porta come la profondità in cui deve essere posizionato il goal goal nella pila di destinazione.
Ogni volta che un goal goal viene posizionato all'indice <= stack_height - altezza goal, ci sarà sempre un percorso più breve verso la vittoria tramite il metodo 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
Dato un po 'di stack tale stack[0] = R
, il gioco è vinto.
GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
Poiché è noto che sono sempre disponibili almeno gli spazi vuoti stack_height, il caso peggiore sarebbe:
[ [ S, S, !Goal ], [R, S, S], [-, -, -]
Poiché sappiamo che il goal goal non può essere nella destinazione goal o il gioco è vinto. Nel qual caso il numero minimo di mosse richieste sarebbero le mosse:
(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
Dato un po 'di stack tale stack[1] = R
, il gioco è vinto.
GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
Sappiamo che ci sono almeno 3 spazi vuoti disponibili, quindi il caso peggiore sarebbe:
[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
In questo caso il numero minimo di mosse sarebbero le mosse:
(1, 2), (0, 2), (0, 2), (1, 0)
Questo vale per tutti i casi.
Pertanto, il problema è stato ridotto al problema di trovare il numero minimo di mosse richieste per posizionare il goal goal all'altezza del goal o sopra.
Ciò divide il problema in una serie di sotto-problemi:
Quando lo stack di destinazione ha il suo pezzo accessibile! = Goal goal, determinando se esiste una posizione valida per quel pezzo, o se il pezzo deve rimanere lì mentre un altro pezzo viene scambiato.
Quando lo stack di destinazione ha il suo pezzo accessibile == goal goal, determinando se può essere rimosso e posizionato all'altezza obiettivo desiderata, o se il pezzo deve rimanere mentre un altro viene scambiato.
Quando i due casi precedenti richiedono un altro pezzo da scambiare, determinare quali pezzi scambiare per aumentare per consentire al pezzo da raggiungere di raggiungere l'altezza.
Lo stack di destinazione deve sempre valutare prima i casi.
IE
stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
Il primo controllo dello stack degli obiettivi porta a:
(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
Ignorare lo stack degli obiettivi:
(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves