gpt4 book ai didi

python - 如何找到将项目移动到堆栈中某个位置的最小移动次数?

转载 作者:行者123 更新时间:2023-12-03 14:44:42 25 4
gpt4 key购买 nike

Stacks

给定一组 NXP 堆栈,其中 N 是堆栈数,P 是堆栈容量,如何计算从位置 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 的算法来寻找问题的路径.这看起来简单得令人沮丧,但我想不出办法让它在指数时间内运行。是否有我遗漏的适用于此的算法?

编辑

我编写了这个函数来计算所需的最小移动次数:
stacks:代表栈中碎片的字符列表,stacks[0][0] 是栈顶[0]
stack_ind:棋子将被添加到的堆栈的索引
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 的要求,我创建了一个 gist对于问题。

当它运行时,它会生成一个随机的堆栈数组,并选择一个需要插入到随机位置的随机堆栈中的随机片段。

运行它会将这种格式的内容打印到控制台。
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 在评论中提到的案例。我最近解决这个问题的方法是开发一组类似于上述规则的规则,并将它们用于分代算法。

规则例如:

永远不要逆转一个 Action 。从 1->0 然后 0->1 (没有意义)

永远不要连续移动一块。从不从 0 -> 1 然后 1 -> 3 移动

给定一些从 stacks[X] 到 stacks[Y] 的移动,然后是一定数量的移动,然后从 stacks[Y] 移动到 stacks[Z],如果 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)

这将适用于所有情况。

因此,问题已简化为寻找最小数量的问题
将球门片放置在球门高度或高于球门高度所需的移动。

这将问题分解为一系列子问题:
  • 当目标栈有它的可访问块 != 目标块时,
    确定该作品是否存在有效位置,或者该作品是否应该
    留在那里,而另一块被交换。
  • 当目标栈有它的可访问块 == 目标块时,
    确定是否可以将其移除并放置在所需的目标高度,或者是否可以
    一块应该留下,而另一个被交换。
  • 当以上两种情况需要交换另一块时,
    确定要交换哪些碎片以增加数量以使其成为可能
    目标块达到目标高度。

  • 目标堆栈应始终首先评估其案例。

    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

    最佳答案

    我想出了两个选项,但没有一个能够及时解决案例 2。第一个选项是使用带有字符串距离度量的 A* 作为 h(n),第二个选项是 IDA*。我测试了许多字符串相似性度量,我在我的方法中使用了 smith-waterman。我已更改您的符号以更快地处理问题。我在每个数字的末尾添加了数字,以检查一块是否移动了两次。

    以下是我测试过的案例:

    start = [
    ['R1', 'R2', 'R3', 'R4'],
    ['Y1', 'Y2', 'Y3', 'Y4'],
    ['G1', 'G2', 'G3', 'G4'],
    ['B1'],
    ['B2', 'B3', 'B4']
    ]

    case_easy = [
    ['R', 'R', 'R', 'R'],
    ['Y', 'Y', 'Y', 'Y'],
    ['G', 'G', 'G'],
    ['B', 'B'],
    ['B', 'B', 'G']
    ]


    case_medium = [
    ['R', 'R', 'R', 'R'],
    ['Y', 'Y', 'Y', 'B'],
    ['G', 'G', 'G'],
    ['B'],
    ['B', 'B', 'G', 'Y']
    ]

    case_medium2 = [
    ['R', 'R', 'R' ],
    ['Y', 'Y', 'Y', 'B'],
    ['G', 'G' ],
    ['B', 'R', 'G'],
    ['B', 'B', 'G', 'Y']
    ]

    case_hard = [
    ['B'],
    ['Y', 'Y', 'Y', 'Y'],
    ['G', 'G', 'G', 'G'],
    ['R','R','R', 'R'],
    ['B','B', 'B']
    ]

    这是 A* 代码:
    from copy import deepcopy
    from heapq import *
    import time, sys
    import textdistance
    import os

    def a_star(b, goal, h):
    print("A*")
    start_time = time.time()
    heap = [(-1, b)]
    bib = {}
    bib[b.stringify()] = b

    while len(heap) > 0:
    node = heappop(heap)[1]
    if node == goal:
    print("Number of explored states: {}".format(len(bib)))
    elapsed_time = time.time() - start_time
    print("Execution time {}".format(elapsed_time))
    return rebuild_path(node)

    valid_moves = node.get_valid_moves()
    children = node.get_children(valid_moves)
    for m in children:
    key = m.stringify()
    if key not in bib.keys():
    h_n = h(key, goal.stringify())
    heappush(heap, (m.g + h_n, m))
    bib[key] = m

    elapsed_time = time.time() - start_time
    print("Execution time {}".format(elapsed_time))
    print('No Solution')

    这是 IDA* 代码:
    #shows the moves done to solve the puzzle
    def rebuild_path(state):
    path = []
    while state.parent != None:
    path.insert(0, state)
    state = state.parent
    path.insert(0, state)
    print("Number of steps to solve: {}".format(len(path) - 1))
    print('Solution')

    def ida_star(root, goal, h):
    print("IDA*")
    start_time = time.time()
    bound = h(root.stringify(), goal.stringify())
    path = [root]
    solved = False
    while not solved:
    t = search(path, 0, bound, goal, h)
    if type(t) == Board:
    solved = True
    elapsed_time = time.time() - start_time
    print("Execution time {}".format(elapsed_time))
    rebuild_path(t)
    return t
    bound = t

    def search(path, g, bound, goal, h):

    node = path[-1]
    time.sleep(0.005)
    f = g + h(node.stringify(), goal.stringify())

    if f > bound: return f
    if node == goal:
    return node

    min_cost = float('inf')
    heap = []
    valid_moves = node.get_valid_moves()
    children = node.get_children(valid_moves)
    for m in children:
    if m not in path:
    heappush(heap, (m.g + h(m.stringify(), goal.stringify()), m))

    while len(heap) > 0:
    path.append(heappop(heap)[1])
    t = search(path, g + 1, bound, goal, h)
    if type(t) == Board: return t
    elif t < min_cost: min_cost = t
    path.pop()
    return min_cost

    class Board:
    def __init__(self, board, parent=None, g=0, last_moved_piece=''):
    self.board = board
    self.capacity = len(board[0])
    self.g = g
    self.parent = parent
    self.piece = last_moved_piece

    def __lt__(self, b):
    return self.g < b.g

    def __call__(self):
    return self.stringify()

    def __eq__(self, b):
    if self is None or b is None: return False
    return self.stringify() == b.stringify()

    def __repr__(self):
    return '\n'.join([' '.join([j[0] for j in i]) for i in self.board])+'\n\n'

    def stringify(self):
    b=''
    for i in self.board:
    a = ''.join([j[0] for j in i])
    b += a + '-' * (self.capacity-len(a))

    return b

    def get_valid_moves(self):
    pos = []
    for i in range(len(self.board)):
    if len(self.board[i]) < self.capacity:
    pos.append(i)
    return pos

    def get_children(self, moves):
    children = []
    for i in range(len(self.board)):
    for j in moves:
    if i != j and self.board[i][-1] != self.piece:
    a = deepcopy(self.board)
    piece = a[i].pop()
    a[j].append(piece)
    children.append(Board(a, self, self.g+1, piece))
    return children

    用法:
    initial = Board(start)
    final1 = Board(case_easy)
    final2 = Board(case_medium)
    final2a = Board(case_medium2)
    final3 = Board(case_hard)

    x = textdistance.gotoh.distance

    a_star(initial, final1, x)
    a_star(initial, final2, x)
    a_star(initial, final2a, x)

    ida_star(initial, final1, x)
    ida_star(initial, final2, x)
    ida_star(initial, final2a, x)

    关于python - 如何找到将项目移动到堆栈中某个位置的最小移动次数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60824715/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com