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

如何在Ruby中快速生成字符串的所有排列?,ruby,math,permutation,Ruby,Math,Permutation,我目前正在使用此函数,代码完全按照它应该的方式工作。 self.chars.permutation.map(&:join).uniq.group\u by(&:chr) 但是,一旦字符串超过10个字符,就需要花费大量时间来生成所有排列。如何更快地生成置换?也许是一种选择。在检查特殊条件之前,它不需要像生成所有置换那样多的内存 比如: 'my_string'.chars.permutation.lazy.map(&:join).each do |permutation| put

我目前正在使用此函数,代码完全按照它应该的方式工作。
self.chars.permutation.map(&:join).uniq.group\u by(&:chr)

但是,一旦字符串超过10个字符,就需要花费大量时间来生成所有排列。如何更快地生成置换?

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

比如:

'my_string'.chars.permutation.lazy.map(&:join).each do |permutation|    
  puts permutation if dictionary.include?(permutation)
end
如果我们看一看,一个11个字母的单词没有重复字母的排列数将是39916800。然而,对于密西西比州来说是11!/( 1! * 4! * 4! * 2!) = 34,650. 第一种方法需要很长时间,但是如果可以使用重复字符来减少搜索空间,那么它可能会变得更易于管理。标准排列方法不会删除重复

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

我目前正在使用此函数,代码完全按照它应该的方式工作。
self.chars.permutation.map(&:join).uniq.group\u by(&:chr)

但是,一旦字符串超过10个字符,就需要花费大量时间来生成所有排列。如何更快地生成置换

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

简言之:甚至尝试加速算法都没有意义。你需要完全避免产生排列。

不需要排列:

  • 对字符串中的字母进行排序
  • 把字典里每个单词的字母分类
  • 查找排序相同的字母
  • 完成了
实施
类字符串
def分类字母
downcase.chars.sort.join
结束
结束
AnagramFinder类
@dict='/usr/share/dict/american english'
类别1}
结束
def分类目录
@已排序的dict | |=生成已排序的dict
结束
私有的
def生成已排序的dict
foreach(@dict).with_object(Hash.new{h,k{h[k]=[])do| word,排序的| dict|
单词,chomp!
已排序的字母[单词.已排序的字母][“印象深刻”,“允许”]
p AnagramFinder.get_anagrams('castor'))
#=>[“卡斯特罗”、“卡斯特罗”、“克罗地亚人”、“演员”、“卡斯特罗”、“配角”、“斯库塔”]
p AnagramFinder.所有最后(5)
#=>
p AnagramFinder.all.max_by(&:长度)
#=>[“Stael”、“Tesla”、“least”、“slate”、“stale”、“steal”、“tales”、“teals”]
这个例子需要在我的slowish服务器上运行0.5s,大部分时间用于构建排序字典。一旦完成,查找几乎是即时的


“Impressives”
有14个字符,您需要非常长的时间才能生成所有排列(14!=87178291200)。

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


我认为你不能。字符串越长,排列就越多。更多的排列需要更多的时间和内存来生成。为什么需要生成这种排列?这可能是解决您问题的更有效的方法。我正在生成排列,以检查字典中是否有字符串的字谜。我想您需要不同的算法。字典有多大?缓存一组字母按字母顺序排序的字典单词可能更划算。如果你这样做,你可以很快地对照集合检查排序的字符串。@mike:你犯的错误和OP一样,你大大低估了n的速度!生长。即使您可以为每个CPU周期生成一个排列,并且您有100个内核,这些内核以5GHz的频率运行,仍然需要一百万年才能为25个字符的字符串生成排列。并行化不是答案,而是一种不同的算法。或者,换一种说法:你是在逐字回答OP的问题(“我如何加速生成置换”),这没关系,但实际上没有意义:不管你如何加速,n!你说得很好,但在最后一句话中只提出了一种可能性。如果你不是在建议一个特定的算法,这应该是一个评论,而不是一个答案。同样,我们有非常相似的解决方案。你基本上在每个问题上都可以使用
每个带有对象的方法:)@Eric,我只学了23个Ruby方法,而
每个带有对象的方法正好是其中之一。仔细选择这23个方法,你可以在需要任何其他方法之前走很长的路!如果
my_string#=>“aardvark”
lazy
会加快查找置换匹配,但不幸的是,该匹配可能是单词
“aardvark
”本身。即使你跳过了单词本身的排列,仍然会有大量的排列来检查最长的单词。将字典转换成集合当然会有帮助,但我不认为计算置换,即使是懒散的,也是一种方法。
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"]
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.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"]}