我想写一个ruby片段,它会接受一个字符串并输出所有可能的大写排列。基本上,我有一个我记得的密码,但我不记得它是如何大写的。
到目前为止,我有以下内容:
def permute(str)
perms = Array.new
(2 ** str.size).times { perms << str }
perms.each_index do |i|
binary = i.to_s(2)
str_arr = perms[i].split(//)
bin_arr = binary.split(//)
while ( bin_arr.size < str_arr.size )
bin_arr.unshift('0')
end
bin_arr.each_index do |b|
str_arr[b].upcase! if bin_arr[b] == '1'
end
puts str_arr.to_s
end
end
这很好用,但我想知道那里是否有任何rubyists可以帮助我改进它,这样它就不必在数字字符串上不必要地工作。
例如,字符串“tst1”生成:
tst1
tst1
tsT1
tsT1
tSt1
tSt1
tST1
tST1
Tst1
Tst1
TsT1
TsT1
TSt1
TSt1
TST1
TST1
我正在寻找的输出是:
tst1
tsT1
tSt1
tST1
Tst1
TsT1
TSt1
TST1
有什么想法吗?
答案 0 :(得分:13)
这是一个很好的机会,将我的“算法推导”课程,即Dijkstra方法,从大学时代转变为实践。这是'干净'版本
require 'set'
def generate_perms(str)
if str.length == 1
return Set.new([str.downcase, str.upcase])
else
head = str[0..0]
tail = str[1..-1]
perms_sub = generate_perms(tail)
d = Set.new(perms_sub.collect{|p| head.downcase + p})
u = Set.new(perms_sub.collect{|p| head.upcase + p})
return d | u
end
end
编辑:Dijkstra告诉我们不要过早优化,所以我认为Array-version最好单独添加:-):
def perms(str)
if str.length == 1
return Array.new([str.downcase, str.upcase])
else
head = str[0..0]
tail = str[1..-1]
perms_sub = perms(tail)
d = perms_sub.collect{|p| head.downcase + p}
u = perms_sub.collect{|p| head.upcase + p}
return (d + u).uniq
end
end
为了让它变得更快,可以借助额外的方法参数转换为尾递归:
# tail recursion version, no stack-overflows :-)
def perms_tail(str, perms)
if str.length == 1
return perms.collect{|p| [p + str.upcase, p+ str.downcase]}.flatten.uniq
else
tail = perms.collect{|p| [p + str[0..0].upcase, p+ str[0..0].downcase]}.flatten
perms_tail(str[1..-1], tail)
end
end
begin
perms_tail("tst1",[""]).each{|p| puts p}
end
现在这实际上非常慢,但尾递归允许简单地重写(自己看看)优化版:
def perms_fast_tail(str)
perms = [""]
tail = str.downcase
while tail.length > 0 do
head, tail, psize = tail[0..0], tail[1..-1], perms.size
hu = head.upcase
for i in (0...psize)
tp = perms[i]
perms[i] = tp + hu
if hu != head
perms.push(tp + head)
end
end
end
perms
end
这有多重要?好吧,让我们运行一些定时测试,以获得乐趣:
begin
str = "Thequickbrownfox"
start = Time.now
perms_tail(str,[""])
puts "tail: #{Time.now - start}"
start2 = Time.now
perms(str)
puts "normal: #{Time.now - start2}"
start3 = Time.now
perms_fast_tail(str)
puts "superfast: #{Time.now - start3}"
end
在我的机器上,这显示了不同之处:
tail: 0.982241
normal: 0.285104
superfast: 0.168895
从非重要的字符串中可以看到速度提升和性能优势; “tst1”将在干净版本中快速运行。因此,Dijkstra是对的:无需优化。虽然这样做很有趣。
答案 1 :(得分:2)
尝试开膛手john,或者cain和able,或任何password cracking software
答案 2 :(得分:2)
你应该创建另一个数组而不是put
只是将它们包含在数组中(如果数组中尚未包含它们)。然后在你的循环之后,用\n
或任何你喜欢的东西加入它们。
def permute(str)
perms = Array.new
correct = []
(2 ** str.size).times { perms << str }
perms.each_index do |i|
binary = i.to_s(2)
str_arr = perms[i].split(//)
bin_arr = binary.split(//)
while ( bin_arr.size < str_arr.size )
bin_arr.unshift('0')
end
bin_arr.each_index do |b|
str_arr[b].upcase! if bin_arr[b] == '1'
end
correct << str_arr.to_s unless correct.include?(str_arr.to_s)
end
puts correct.join("\n")
end
输出:
>> permute("tst1")
tst1
tsT1
tSt1
tST1
Tst1
TsT1
TSt1
TST1
答案 3 :(得分:1)
另一种解决方案(谁能抗拒尝试呢?):
require 'pp'
class String
def permute
return [self, self.upcase].uniq if size <= 1
[self[0..0], self[0..0].upcase].uniq.map do |x|
self[1..-1].permute.map {|y| x+y}
end.flatten
end
end
pp 'tst1'.permute
返回[“tst1”,“tsT1”,“tSt1”,“tST1”,“Tst1”,“TsT1”,“TSt1”,“TST1”]
答案 4 :(得分:0)
好吧,我不知道ruby,所以我可能错了,但在我看来代码是有效的。这只是因为当您进行大写排列时,您不会将数字考虑在内。数字一只有一个版本,所以大写看起来一样。因此:“tst1”和“tst1”,“tsT1”和“tsT1”等等......
您是否尝试使用“acb”代码?这样可以正常工作还是会遇到同样的问题?
答案 5 :(得分:0)
一种简单的方法可能是从字符串中删除数字,将结果传递给您已编写的函数,然后将数字放回到同一索引中。
答案 6 :(得分:0)
也许不是最优雅的解决方案,但你可以改变
puts str_arr.to_s
到
passwords << str_arr.to_s
并在循环之后
puts passwords.uniq
答案 7 :(得分:0)
def perm(s)
s2 = s.upcase
n = s.size
ary = [s.split(//), s2.split(//), (0..(n-1)).map{|i| 2**i}.reverse].transpose
(0..(2**n-1)).map{|i| ary.map{|a, b, c| ((c & i) > 0) ? b : a }.join }.uniq
end
p perm('tst1')
# ["tst1", "tsT1", "tSt1", "tST1", "Tst1", "TsT1", "TSt1", "TST1"]
答案 8 :(得分:0)
#!/usr/local/bin/ruby
$result = []
def permute(str)
perms = Array.new
(2 ** str.size).times { perms << str }
perms.each_index do |i|
binary = i.to_s(2)
str_arr = perms[i].split(//)
bin_arr = binary.split(//)
while ( bin_arr.size < str_arr.size )
bin_arr.unshift('0')
end
bin_arr.each_index do |b|
str_arr[b].upcase! if bin_arr[b] == '1'
end
$result << str_arr.to_s
end
$result
end
puts permute(ARGV[0]).uniq