【问题标题】:Find all the possible permutations using Ruby and recursion使用 Ruby 和递归查找所有可能的排列
【发布时间】:2014-08-10 00:12:23
【问题描述】:

我一直在尝试解决一个简单的测验问题,以使用 Ruby 和递归找到字符串的所有可能排列。

我有以下 Ruby 代码:

def permutation(string)
  return [string] if string.size < 2

  chr    = string.chars.first
  perms  = permutation(string[1..-1])

  result = []

  for perm in perms
    for i in (0..perm.size)
      result << (perm[0..i] + chr + perm[i..-1])
    end
  end

  return result  
end

每当我尝试使用 puts permutation("abc") 测试代码时,我都会得到以下输出:

cacbc
cbabc
cbcac
cbca
cacb
cbab
cba

从理论上讲,这应该是一个非常简单直接的问题,但我确定我做错了什么。很可能它与循环的范围有关。而且我知道 Ruby Array 类有实例方法 permutation 可以做到这一点,但我正在尝试解决它以进行练习。

请注意,当前实现的复杂度为 O(N!)。有没有办法进一步提高性能?

【问题讨论】:

    标签: ruby arrays


    【解决方案1】:

    要看看有什么困难,让我们用一个更简单的例子来试试吧:

    string = "ab"
    

    您想要的结果是["ab", "ba"]。让我们看看你得到了什么:

    string.size #=> 2
    

    所以我们什么时候不回来

    return [string] if string.size < 2
      #=> return ["ab"] if "ab".size < 2
    

    被执行。

    接下来我们计算:

    chr = string.chars.first #=> "a"
    

    请注意,更直接的计算方法如下:

    chr = string[0]  #=> "a"
    

    或者,更好的是,使用String#chr

    chr = string.chr #=> "a"
    

    后者说明了为什么chr 不是变量名的最佳选择。

    下一个

    perms = permutation(string[1..-1])
      #=> = permutation("b")
    

    我现在将缩进返回值以强调我们第二次调用permutationpermuation的论据是:

      string #=> "b"
    

    现在当我们执行时:

      return [string] if string.size < 2
      #=> return ["b"] if "b".size < 2
    

    我们返回["b"],所以(回到原来对permutation的调用):

    perms = ["b"]
    

    chr =&gt; "a" 一起使用,之前计算过。下一个:

    result = []
    
    for perm in perms
      for i in (0..perm.size)
        result << (perm[0..i] + chr + perm[i..-1])
      end
    end
    

    由于perms 仅包含单个元素"b",因此两个for 循环简化为:

    for i in (0.."b".size)
      result << ("b"[0..i] + "a" + "b"[i..-1])
    end
    

    这是:

    for i in (0..1)
      result << ("b"[0..i] + "a" + "b"[i..-1])
    end
    

    请注意,"b"[0..0]"b"[0..1]"b"[0..-1] 都等于 "b"[0],也就是 "b""b"[1..-1] #=&gt; ''。因此,当i =&gt; 0时,我们执行:

    result << ("b"[0..0] + "a" + "b"[0..-1])
      #=> result << ("b" + "a" + "b")
      #=> result << "bab"
    

    i =&gt; 1:

    result << ("b"[0..1] + "a" + "b"[1..-1])
      #=> result << ("b" + "a" + "")
      #=> result << "ba"
    

    所以:

    result => ["bab" + "ba"]
    

    这显然不是你想要的。

    您需要做的是将双 for 循环更改为:

    for perm in perms
      result << chr + perm
      for i in (1..perm.size-1)
        result << (perm[0..i-1] + chr + perm[i..-1])
      end
      result << perm + chr
    end
    

    使用String#insert方法可以写得更简洁:

    for perm in perms
      for i in (0..perm.size)
        result << perm.dup.insert(i,chr)
      end
    end
    

    你通常会看到这样写:

    perms.each_with_object([]) do |perm, result|
      (0..perm.size).each { |i| result << perm.dup.insert(i,chr) }
    end
    

    请注意,在发送insert 之前,我们必须先.dup 字符串,因为insert 会修改字符串。

    这样做,你不需要result = []。也不需要return result,因为parms.each_with_object 返回result,如果没有return 语句,该方法返回最后计算的数量。此外,您不需要临时变量 perms(或 ch,如果需要)。

    总而言之,我们有:

    def permutation(string)
      return [string] if string.size < 2
      ch = string[0]
      permutation(string[1..-1]).each_with_object([]) do |perm, result|
        (0..perm.size).each { |i| result << perm.dup.insert(i,ch) }
      end
    end
    

    让我们试试吧:

    permutation("ab")
      #=> ["ab", "ba"]
    permutation("abc")
      #=> ["abc", "bac", "bca", "acb", "cab", "cba"]
    permutation("abcd")
      #=> ["abcd", "bacd", "bcad", "bcda", "acbd", "cabd",
      #    "cbad", "cbda", "acdb", "cadb", "cdab", "cdba",
      #    "abdc", "badc", "bdac", "bdca", "adbc", "dabc",
      #    "dbac", "dbca", "adcb", "dacb", "dcab", "dcba"]
    

    Eki,你是图中的哪一个?

    【讨论】:

    • 哇,好帮手。这正是我试图调试的 :) 非常感谢兄弟。我是中间的那个人:) 我的女朋友和我最好的朋友和我在一起:) 干杯伙伴
    • StackOverFlow 是一个美丽的地方,因为有像你这样的美丽的人。像一首诗一样的答案...谢谢!
    【解决方案2】:

    你可以使用Array#permutation:

    def permutation(string)
      string.permutation(string.size).to_a
    end
    
    permutation('abc'.chars)
    # => [["a", "b", "c"], ["a", "c", "b"], ["b", "a", "c"], ["b", "c", "a"],
    #     ["c", "a", "b"], ["c", "b", "a"]]
    

    更新没有使用Array#permutation

    def permutation(string)
      return [''] if string.empty?
    
      chrs = string.chars
      (0...string.size).flat_map { |i|
        chr, rest = string[i], string[0...i] + string[i+1..-1]
        permutation(rest).map { |sub|
          chr + sub
        }
      }
    end
    
    permutation('abc')
    # => ["abc", "acb", "bac", "bca", "cab", "cba"]
    

    【讨论】:

    • 感谢您的帮助。但正如我提到的,我知道 Array#permutation,我在帖子中提到了这一点。我只是想弄清楚我的代码有什么问题。
    猜你喜欢
    • 2016-04-06
    • 2015-08-11
    • 2012-05-05
    • 1970-01-01
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    相关资源
    最近更新 更多