如何使用更少的内存生成数组排列?

时间:2013-02-02 23:04:38

标签: ruby

所以我需要得到一个字符串的所有可能的排列。

我现在拥有的是:

def uniq_permutations string
  string.split(//).permutation.map(&:join).uniq
end

好的,现在我的问题是:这个方法适用于小字符串,但我希望能够使用大小为15或甚至20的字符串。并且使用这种方法它会占用大量内存(> 1gb)我的问题是我可以改变什么不使用那么多内存?

有更好的方法来生成排列吗?我应该在文件系统中保留它们并在需要时检索它们(我希望不是因为这可能会使我的方法变慢)?

我该怎么办?

更新

我实际上不需要将结果保存在我只需要在表中查找每个结果的任何位置以查看它是否存在。

4 个答案:

答案 0 :(得分:4)

重申萨瓦所说的话。你了解范围吗?任何n元素的排列数为n!。这是关于你可以获得的最激进的数学进展操作。 {1-}在1-20之间的结果是:

n

最后一个数字约为2亿元,即2亿亿。

那是2265820000千兆字节。

您可以整天将结果保存到磁盘 - 除非您拥有世界上所有的Google数据中心,否则您将非常幸运:)

答案 1 :(得分:4)

您对map(&:join)的调用是在内存中创建数组的内容,因为map实际上将枚举器转换为数组。根据您的目的,您可以避免使用以下内容创建数组:

def each_permutation(string)
  string.split(//).permutation do |permutaion|
    yield permutation.join
  end
end

然后使用这样的方法:

each_permutation(my_string) do |s|
  lookup_string(s) #or whatever you need to do for each string here
end

这不检查重复项(不调用uniq),但避免创建数组。对于大字符串,这可能需要相当长的时间。

但是我怀疑在你的情况下有更好的方法来解决你的问题。

  

我实际上不需要将结果保存在我只需要在表中查找每个结果的任何位置以查看它是否存在。

看起来你正在寻找现有单词列表中字符串的可能字谜。如果您使用任何两个字谜并对其中的字符进行排序,则生成的两个字符串将是相同的。你是否可以改变你的数据结构,以便你有一个哈希,键是排序的字符串,值是一个单词列表,是该字符串的字谜。然后,不是检查列表中新字符串的所有排列,而只需要对字符串中的字符进行排序,并使用该字符串作为键来查找作为该字符串排列的所有字符串的列表。

答案 2 :(得分:4)

也许您不需要生成集合的所有元素,而只需要生成随机或约束子集。我编写了一个算法来在O(n)时间内生成m- th 置换。

首先将密钥转换为the factorial number system中自身的列表表示。然后迭代地在指定的每个索引上拉出项目新列表和 旧列表。

module Factorial
  def factorial num; (2..num).inject(:*) || 1; end

  def factorial_floor num
    tmp_1 = 0
    1.upto(1.0/0.0) do |counter|
      break [tmp_1, counter - 1] if (tmp_2 = factorial counter) > num
      tmp_1 = tmp_2     #####
    end                # # 
  end                 #   #
end                        # returns [factorial, integer that generates it]
                            # for the factorial closest to without going over num

class Array; include Factorial
  def generate_swap_list key   
    swap_list = []              
    key -= (swap_list << (factorial_floor key)).last[0] while key > 0
    swap_list
  end

  def reduce_swap_list swap_list
    swap_list = swap_list.map   { |x|       x[1]                    }
    ((length - 1).downto 0).map { |element| swap_list.count element }
  end

  def keyed_permute key
    apply_swaps reduce_swap_list generate_swap_list key
  end

  def apply_swaps swap_list
    swap_list.map { |index| delete_at index }
  end
end

现在,如果你想随机抽样一些排列,ruby附带Array.shuffle!,但这可以让你复制和保存排列或迭代permutohedral space。或者也许有一种方法可以为您的目的约束排列空间。

constrained_generator_thing do |val|
    Array.new(sample_size) {array_to_permute.keyed_permute val}
end

答案 3 :(得分:0)

也许我错过了明显的,但为什么不这样做

['a','a','b'].permutation.to_a.uniq!