N'nin yığın sayısı ve P'nin yığın kapasitesi olduğu bir dizi NXP yığını göz önüne alındığında, A konumundaki bir düğümden bazı rastgele B konumuna geçmek için gereken minimum swap sayısını nasıl hesaplayabilirim? Bir oyun tasarlıyorum ve son amaç tüm yığınları aynı renkte olacak şekilde sıralamak.
# 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']
]
Ben stacks[1][1]
böyle bir "B" eklemek istiyorum stacks[1] = ["-", "B", "Y", "Y"]
. Bunun için gereken minimum hareket sayısını nasıl belirleyebilirim?
Birden fazla yaklaşıma baktım, bir durumdan tüm olası hareketleri üreten, onları puanlayan ve daha sonra en iyi puanlama yollarını devam ettiren genetik algoritmaları denedim, ayrıca problem üzerinde yol bulma için Djikstra'nın algoritmasını çalıştırmaya çalıştım . Sinir bozucu bir şekilde basit görünüyor, ancak üstel zamandan başka bir şeyde çalıştırılmasını sağlayacak bir yol bulamıyorum. Burada uygulanabilecek bir algoritma var mı?
Düzenle
Gerekli minimum hareket sayısını hesaplamak için bu işlevi yazdım: yığınlar: Yığındaki parçaları temsil eden Karakterler Listesi, yığınlar [0] [0] yığının üst kısmı [0] stack_ind: parçasının needs_piece öğesine ekleneceği yığın: Stack'e eklenecek parça needs_index: Parçanın yerleştirilmesi gereken dizin
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")
Düzenleme: Yığınlardaki Test Durumları:
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
Gerçek kod uygulaması zor olan kısım değil, mücadele ettiğim problemi çözen bir algoritmanın nasıl uygulanacağını belirliyor.
YonIif isteği @ gereğince bir oluşturduk özünü sorun.
Çalıştığında, rastgele bir yığın dizisi oluşturur ve rastgele bir konuma rastgele bir yığına yerleştirilmesi gereken rastgele bir parça seçer.
Çalıştırılması konsolda bu formatta bir şey yazdırır.
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']
Durum güncelleme
Bu sorunu bir şekilde çözmeye çok kararlıyım .
Yorumlarda bahsedilen @Hans Olsson gibi davaların sayısını en aza indirmenin bir yolu olduğunu unutmayın. Bu soruna en son yaklaşımım, bahsedilenlere benzer bir dizi kural geliştirmek ve bunları nesilsel bir algoritmada kullanmaktı.
Gibi kurallar:
Bir hareketi asla tersine çevirme. 1-> 0'dan sonra 0-> 1'den gidin (Anlamsız)
Bir parçayı arka arkaya iki kez hareket ettirmeyin. Asla 0 -> 1 sonra 1 -> 3
Yığınlar [X] 'dan yığınlara [Y] bir miktar hareket verildiğinde, bir miktar hamle, daha sonra yığınlar [Y]' dan yığınlar [Z] 'a bir hareket verildiğinde, eğer yığınlar [Z] hareket sırasındaki durumdaysa yığınlardan [X] yığınlara [Y] gerçekleşti, yığınlardan [X] doğrudan yığınlara [Z] geçilerek bir hareket ortadan kaldırılabilirdi
Şu anda, bu soruna bir kuşak algoritması kullanılarak hesaplanabilecek kadar "geçerli" hareket sayısını en aza indirecek yeterli kurallar yaratma çabasıyla yaklaşıyorum. Birisi ek kurallar düşünebilirse, yorumlarda duymak isterim.
Güncelleme
@RootTwo tarafından verilen cevap sayesinde burada özetleyeceğim bir dönüm noktası yaşadım.
Atılım üzerine
Hedef yüksekliğini, hedef parçasının hedef yığınına yerleştirilmesi gereken derinlik olarak tanımlayın.
<= Stack_height - goal height dizinine bir hedef parçası yerleştirildiğinde, clear_path () yöntemi ile her zaman zafere giden en kısa yol olacaktır.
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
Öyle bir yığın verilirse stack[0] = R
, oyun kazanılır.
GOAL
[ [ (S | -), (S | -), (S | -) ], [R, S, S], [(S | - ), (S | -), (S | -)] ]
Her zaman en az stack_height boş alan olduğu bilindiği için, mümkün olan en kötü durum şöyle olur:
[ [ S, S, !Goal ], [R, S, S], [-, -, -]
Gol parçasının gol hedefinde olamayacağını bildiğimiz için oyun kazanılır. Bu durumda gereken minimum hamle sayısı hamle olacaktır:
(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
Öyle bir yığın verilirse stack[1] = R
, oyun kazanılır.
GOAL
[ [ (S | -), (S | -), S], [ (S | -), R, S], [(S | -), (S | -), (S | -)]
En az 3 boş alan olduğunu biliyoruz, bu yüzden mümkün olan en kötü durum şöyle olacaktır:
[ [ S, !Goal, S], [S, R, S], [ -, -, - ]
Bu durumda minimum hamle sayısı hamle olacaktır:
(1, 2), (0, 2), (0, 2), (1, 0)
Bu, tüm durumlar için geçerli olacaktır.
Böylece, sorun, hedef parçayı hedef yüksekliğine veya üstüne yerleştirmek için gereken minimum hareket sayısını bulma sorununa indirgenmiştir.
Bu, sorunu bir dizi alt soruna böler:
Hedef yığının erişilebilir parçası! = Hedef parçası olduğunda, o parça için geçerli bir yer olup olmadığını veya başka bir parça değiştirilirken parçanın orada kalması gerekip gerekmediğini belirleme.
Hedef yığının erişilebilir parçası == hedef parçası olduğunda, kaldırılıp gereken hedef yüksekliğine yerleştirilip yerleştirilemeyeceğini veya bir başkası takas edilirken parçanın kalması gerekip gerekmediğini belirleme.
Yukarıdaki iki durum başka bir parçanın değiştirilmesini gerektirdiğinde, hedef parçanın hedef yüksekliğine ulaşmasını sağlamak için hangi parçaların değiştirileceğini belirlemek için.
Hedef yığını her zaman önce durumlarını değerlendirmelidir.
IE
stacks = [ [-, R, G], [-, R, G], [-, R, G] ]
Goal = stacks[0][1] = G
Önce Hedef Yığını kontrol edilir:
(0, 1), (0, 2), (1, 0), (2, 0) = 4 Moves
Hedef Yığını Yok Sayma:
(1, 0), (1, 2), (0, 1), (0, 1), (2, 0) = 5 Moves