在Ruby中骑士之旅遇到麻烦

时间:2017-07-18 12:12:44

标签: ruby algorithm knights-tour

我试图将骑士旅行问题作为递归练习,因为我有一段时间没有使用它,但我的脚本似乎没有找到解决方案,只有高达56步。任何向我指出正确方向的提示都将不胜感激。

class KnightsTour
def initialize
    board = [nil, nil, nil, nil, nil, nil, nil, nil]
    8.times do |i|
        board[i] = [0, 0, 0, 0, 0, 0, 0, 0]
    end
    tour(0,0,board)
end

def tour(x,y,board,current_move=0)
    puts board if current_move == 64

    return if board[x] == nil || 
              board[x][y] == nil || 
              board[x][y] != 0 || 
              x < 0 ||
              y < 0 ||
              current_move == 64

    current_move +=1
    board[x][y] = current_move

    tour(x+2, y+1, board.dup, current_move)
    tour(x+2, y-1, board.dup, current_move)
    tour(x+1, y-2, board.dup, current_move)
    tour(x-1, y-2, board.dup, current_move)
    tour(x-1, y+2, board.dup, current_move)
    tour(x+1, y+2, board.dup, current_move)
    tour(x-2, y+1, board.dup, current_move)
    tour(x-2, y-1, board.dup, current_move)
end
end

KnightsTour.new

2 个答案:

答案 0 :(得分:1)

主要问题是您只对所有递归使用一个board对象。您可以在尝试移动时随时使用board的副本。 dup生成浅拷贝,并不足以复制棋盘。

另一个问题可能是由于指数增长而导致暴力方法太慢(每次迭代都会有8次移动,即使你提前停止了一些)。

通常的做法是选择具有最少可能性的细胞作为下一步。

答案 1 :(得分:0)

def is_safe?(solution, x, y)
   x >= 0 && x < solution.length && y>= 0 && y < solution.length && solution[x][y] == -1
end

def solve_nt_helper(size, solution, curr_x, curr_y, num, xmoves, ymoves)
    return true if num == size * size
    size.times.each do |i|
        # p "i:#{i}"
        next_x = curr_x + xmoves[i]
        next_y = curr_y + ymoves[i]
        if is_safe?(solution, next_x, next_y)
            solution[next_x][next_y] = num
            return true if solve_nt_helper(size, solution, next_x, next_y, num + 1, xmoves, ymoves)
  
            solution[next_x][next_y] = -1
        end
    end
    # p "failed at num #{num}"
    false
end

def solve_nt(n)
    xmoves = [2, 1, -1, -2, -2, -1, 1, 2]
    ymoves = [1, 2, 2, 1, -1, -2, -2, -1]
    solution = Array.new(n) { Array.new(n) { -1 } }
    solution[0][0] = 0
    solution.each do |row|
      p row
    end
    return 'invalid' unless solve_nt_helper(n, solution, 0, 0, 1, xmoves, ymoves)
    solution.each do |row|
      p row
    end
    solution
end

n = 8
require 'memory_profiler'
report = MemoryProfiler.report do
solve_nt(n)
end
report.pretty_print

这是有效的红宝石版本。该解决方案的诀窍是

  1. 不要在递归方法中放置任何调试消息。
  2. 将xmoves和ymoves作为变量传递给递归方法。不要定义常量 KNIGHTS_MOVES = [[2,1],[2,-1],[-2,1],[-2,-1],[1,2],[1,-2],[-1,2] ,[-1,-2]] 并在solve_nt_helper中对其进行迭代。 size.each每个对象都会生成许多Enumerator对象,但对常量进行迭代似乎会使整个过程变慢。

技巧1似乎也适用于java和python.did不要尝试将item2用于java和python。该算法是从geekstogeeks

借用的