如何在Ruby中快速生成字符串的所有排列?

时间:2017-01-01 17:47:07

标签: ruby math permutation

我目前正在使用此功能,代码完全正常工作 self.chars.permutation.map(&:join).uniq.group_by(&:chr)

但是,一旦字符串超过10个字符,生成所有排列需要花费大量时间。我怎样才能更快地生成排列?

5 个答案:

答案 0 :(得分:2)

  

我目前正在使用此功能,代码完全正常工作   self.chars.permutation.map(&:join).uniq.group_by(&:chr)

     

但是,一旦字符串超过10个字符,生成所有排列需要花费大量时间。我怎样才能更快地生成排列?

你不能。好吧,也许有一些方法可以加快它的速度,但实际上没有意义:排列的数量太大了。对于只有25个字符,即使我们假设您可以为每个CPU周期生成一个排列,即使我们假设您有一个5GHz CPU,即使我们假设您的CPU有100个内核,即使我们假设工作可以完全分布在这些核心之间,它仍然需要接近一个百万年来生成。只有那么多人。

简而言之:即使尝试加速算法也没有意义。你需要完全避免产生排列。

答案 1 :(得分:1)

也许lazy可能是一种选择。在检查特殊条件之前,它不需要像生成所有排列那样多的内存。

类似的东西:

'my_string'.chars.permutation.lazy.map(&:join).each do |permutation|    
  puts permutation if dictionary.include?(permutation)
end

答案 2 :(得分:1)

如果我们看一下Permutation,我们会看到没有重复字母的十一个字母单词的排列数量为39,916,800。然而对于密西西比来说它是11! /(1!* 4!* 4!* 2!)= 34,650。第一个是你需要很长时间,但是如果你可以使用重复的字符减少搜索空间,它可能会变得更易于管理。标准置换方法不会删除重复。

搜索“不重复的ruby排列”可能会出现一些算法。

答案 3 :(得分:1)

理论

无需排列:

  • 对字符串中的字母进行排序
  • 对字典中每个单词的字母进行排序
  • 查找相同的已排序字母
  • 完成!

实施

class String
  def sorted_letters
    downcase.chars.sort.join
  end
end

class AnagramFinder
  @dict = '/usr/share/dict/american-english'
  class << self
    def get_anagrams(word)
      sorted_dict[word.sorted_letters]
    end

    def all
      sorted_dict.values.select { |anagrams| anagrams.size > 1 }
    end

    def sorted_dict
      @sorted_dict ||= generate_sorted_dict
    end

    private

    def generate_sorted_dict
      File.foreach(@dict).with_object(Hash.new { |h, k| h[k] = [] }) do |word, sorted_dict|
        word.chomp!
        sorted_dict[word.sorted_letters] << word
      end
    end
  end
end

p AnagramFinder.get_anagrams('impressiveness')
#=> ["impressiveness", "permissiveness"]
p AnagramFinder.get_anagrams('castor')
#=> ["Castor", "Castro", "Croats", "actors", "castor", "costar", "scrota"]
p AnagramFinder.all.last(5)
#=> [["wist", "wits"], ["withers", "writhes"], ["woodworm", "wormwood"], ["wriest", "writes"], ["wrist", "writs"]]
p AnagramFinder.all.max_by(&:length)
#=> ["Stael", "Tesla", "least", "slate", "stale", "steal", "tales", "teals"]

这个例子在我缓慢的服务器上需要0.5秒,而且大部分用于构建排序字典。一旦完成,查找几乎是即时的。

"impressiveness"有14个字符,您需要非常长时间来生成所有排列(14!= 87178291200)。

答案 4 :(得分:1)

不是计算每个单词的所有排列,更好的方法是首先从字典创建一个散列,其字符串是按字符排序的字符串,其值是包含字典中所有字的数组,这些字是键的字谜。当单词在字典中不包含字谜时(不是自身),数组为空。

words      = %w| god act bat tar a lion stop |
  #=> ["god", "act", "bat", "tar", "a", "lion", "stop"]
dictionary = %w| cat dog a fowl bat god act lion pig donkey loin post pots
                 spot stop tops| 
  #=> ["cat", "dog", "a", "fowl", "bat", "god", "act", "lion", "pig",
  #    "donkey", "loin", "post", "pots", "spot", "stop", "tops"]

h = dictionary.each_with_object(Hash.new { |h,k| h[k] = [] }) do |w,h|
  h[w.each_char.sort.join] << w
end
  #=> {"act"=>["cat", "act"], "dgo"=>["dog", "god"], "a"=>["a"], "flow"=>["fowl"],
  #    "abt"=>["bat"], "ilno"=>["lion", "loin"], "gip"=>["pig"], "deknoy"=>["donkey"],
  #    "opst"=>["post", "pots", "spot", "stop", "tops"]} 

然后我们可以通过对其字符进行排序并查看是否是哈希中的键来获取words中每个单词的所有字谜。

words.each_with_object({}) do |w,g|
  key = w.downcase.chars.sort.join
  values = h.key?(key) ? (h[key]-[w]) : []
  g[w] = values
end
  #=> {"god"=>["dog"], "act"=>["cat"], "bat"=>[], "tar"=>[], "a"=>[],
  #    "lion"=>["loin"], "stop"=>["post", "pots", "spot", "tops"]}