资本化排列

时间:2009-09-07 16:41:23

标签: ruby permutation capitalization

我想写一个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

有什么想法吗?

9 个答案:

答案 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