递归解决方案不能正确迭代

时间:2015-06-15 13:43:10

标签: ruby algorithm search recursion

我正在解决Ruby中的玩具问题:如何生成所有可能的10位数电话号码,其中每个连续数字与键盘上的最后一个数字相邻。我已经表示了数字之间的相邻关系,并且具有递归函数,但我的方法并没有遍历整个解空间。它只是找到第一个解决方案并返回。

这是我的代码:

adjacencies = { 1 => [2, 4],
                2 => [1, 3, 5],
                3 => [2, 6],
                4 => [1, 5, 7],
                5 => [2, 4, 6, 8],
                6 => [3, 5, 9],
                7 => [4, 8],
                8 => [5, 7, 9, 0],
                9 => [6, 8],
                0 => [8]
              }

def append_number(partial_phone_number, to_append, adjacencies)
  phone_length = 10
  partial_phone_number = partial_phone_number + to_append.to_s
  if (partial_phone_number.length == phone_length)
    return partial_phone_number
  else
    adjacencies[to_append].each do |a|
      return append_number(partial_phone_number, a, adjacencies)
    end
  end
end

(0..9).each do |n|
  puts append_number("", n, adjacencies)
end

这是运行时的输出:

0852121212
1212121212
2121212121
3212121212
4121212121
5212121212
6321212121
7412121212
8521212121
9632121212

3 个答案:

答案 0 :(得分:2)

第一次输入adjacencies[to_append].each时,您立即从方法中return,因此循环永远不会被执行多次。

你需要

  1. 返回电话号码列表,而不只是一个电话号码
  2. 在递归调用中以某种方式构建该列表

答案 1 :(得分:1)

以下是递归方法的修改。 FIRST_DIGITn个数字电话号码的可能第一个数字的数组,n是方法recurse的第一个参数。您希望确定recurse(10)

ADJ = { 1 => [2, 4],
        2 => [1, 3, 5],
        3 => [2, 6],
        4 => [1, 5, 7],
        5 => [2, 4, 6, 8],
        6 => [3, 5, 9],
        7 => [4, 8],
        8 => [5, 7, 9, 0],
        9 => [6, 8],
        0 => [8]
      }

FIRST_DIGIT = (1..9).to_a
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

def recurse(n, nxt=FIRST_DIGIT)
  nxt.each_with_object([]) do |i,a|
    is = i.to_s
    if n==1
      a << is
    else
      recurse(n-1, ADJ[i]).each { |s| a << is + s }
    end
  end
end

recurse 1
  #=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"] 
recurse 2
  #=> ["12", "14", "21", "23", "25", "32", "36", "41", "45", 
  #    "47", "52", "54", "56", "58", "63", "65", "69",
  #    "74", "78", "85", "87", "89", "80", "96", "98"] 
recurse 3
  #=> ["121", "123", "125", "141", "145", "147",
  #    "212", "214", "232", "236", "252", "254", "256", "258",
  #    "321", "323", "325", "363", "365", "369",
  #    "412", "414", "452", "454", "456", "458", "474", "478",
  #    "521", "523", "525", "541", "545", "547", "563", "565",
  #    "569", "585", "587", "589", "580",
  #    "632", "636", "652", "654", "656", "658", "696", "698",
  #    "741", "745", "747", "785", "787", "789", "780",
  #    "852", "854", "856", "858", "874", "878", "896", "898", "808",
  #    "963", "965", "969", "985", "987", "989", "980"] 
recurse(10).size
  #=> 117529 

[编辑: OP询问了修改代码以避免循环的可能性。这并不困难。也可以使用相同的修改来强制执行其他规则(例如,没有666),所有这些都将减少要考虑的组合的数量。我们可以通过向so_far添加一个参数recurse来实现这一点,该参数是到目前为止所选数字的一个数组(或者它可以是一个字符串):

def recurse(n, so_far=[], nxt=FIRST_DIGIT)
  nxt.each_with_object([]) do |i,a|
    is = i.to_s
    if n==1
      a << is
    else
      < construct array 'permitted' from ADJ[i] and other rules > 
      recurse(n-1, so_far+[i], permitted).each { |s| a << is + s }
    end
  end
end

请注意,使用默认值的两个参数不是问题,因为recurse最初只使用第一个参数调用,然后将使用所有三个参数调用。

答案 2 :(得分:0)

return迭代器中的each语句在第一次迭代时退出递归调用。不要在那里使用return。一种可能的解决方案是在到达递归基本情况时将结果附加到列表(通过参数传递)。

相关问题