【问题标题】:Capitalization Permutations大写排列
【发布时间】:2009-09-07 16:41:22
【问题描述】:

我想写一个 ruby​​ 的 sn-p,它将接受一个字符串并输出所有可能的大写排列。基本上,我有一个我记得的密码,但我不记得它是如何大写的。

到目前为止,我有以下内容:

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

这很好用,但我想知道是否有任何 ruby​​ 专家可以帮助我改进它,这样它就不必在带有数字的字符串上不必要地工作了。

例如,字符串“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

有什么想法吗?

【问题讨论】:

    标签: ruby permutation capitalization


    【解决方案1】:

    这是一个很好的机会,可以将我在大学时代的“算法推导”课程,即 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 是对的:不需要优化。虽然这样做很有趣。

    【讨论】:

    • Dijkstra 方法的最大好处是没有人破坏/触摸您的代码,因为它太密集而无法阅读。你不能玩弄它,你必须在篡改它之前“摸索”它:-)
    【解决方案2】:

    试试 john the ripper,或 cain andable,或任何password cracking software

    【讨论】:

    • 这与手头的问题有什么关系?
    • 嗯,她是在解决问题,而不是专注于问题。不错:-)
    【解决方案3】:

    您应该创建另一个数组,而不是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
    

    【讨论】:

    • 为什么这被否决了,它正是提出问题的人想要的?
    【解决方案4】:

    另一种解决方案(谁能抗拒尝试?):

    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"]

    【讨论】:

    • permute 定义的第一行中的 return 可以改进为“return [self.downcase, self.upcase].uniq if size
    • 而第二行permute的定义可以简化为“self[0..0].permute.map do |x|” (更短,但稍慢)。
    【解决方案5】:

    好吧,我不知道 ruby​​,所以我可能是错的,但在我看来代码有效。只是因为您在进行大写排列时没有考虑数字。数字一只有一个版本,所以大写看起来是一样的。因此:“tst1”和“tst1”,“tsT1”和“tsT1”等等..

    您是否尝试过带有“acb”的代码?这工作正常还是遇到同样的问题?

    【讨论】:

    • 那为什么不简单地设置一个不置换数字的条件呢?
    【解决方案6】:

    一种简单的方法可能是从字符串中删除数字,将结果传递给您已经编写的函数,然后将数字放回相同的索引处。

    【讨论】:

      【解决方案7】:

      也许不是最优雅的解决方案,但你可以改变

      puts str_arr.to_s
      

      passwords << str_arr.to_s
      

      循环之后

      puts passwords.uniq
      

      【讨论】:

        【解决方案8】:

        对原始程序的轻微修改

        #!/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
        

        【讨论】:

          【解决方案9】:

          喜欢这样吗?

          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 行输出。
          • 是的,但只有在密码以单个数字结尾时才有效。
          • 一位数..我不关注?
          猜你喜欢
          • 1970-01-01
          • 2021-12-15
          • 2011-10-10
          • 1970-01-01
          • 2021-03-20
          • 2012-12-04
          • 2014-11-12
          • 2020-12-27
          • 1970-01-01
          相关资源
          最近更新 更多