我一直试图解决一个简单的测验问题,找到使用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!)。反正有进一步提升性能吗?
答案 0 :(得分:12)
要了解困难可能是什么,让我们尝试一个更简单的例子:
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")
我现在将缩进返回值以强调我们第二次调用permutation
。 permuation
的论点是:
string #=> "b"
现在我们执行:
return [string] if string.size < 2
#=> return ["b"] if "b".size < 2
我们返回["b"]
,所以(回到permutation
的原始号召):
perms = ["b"]
与之前计算的chr => "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] #=> ''
。因此,当i => 0
时,我们执行:
result << ("b"[0..0] + "a" + "b"[0..-1])
#=> result << ("b" + "a" + "b")
#=> result << "bab"
以及i => 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
请注意,在发送.dup
之前我们必须insert
字符串,因为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,你在图片中有哪一个?
答案 1 :(得分:6)
您可以使用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"]]
UPDATE 不使用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"]