是否可以使用Array#product方法替代方法?

时间:2019-03-24 19:03:04

标签: ruby-on-rails ruby rails-activerecord

我正在使用方法Array#product和几个大数组参数。接收器也是一个大阵列。因此,结果数组很大,并占用了过多的内存。我需要产生的所有组合,但我不需要它们在单个阵列中。

我想知道是否有替代方法可以使用更少的内存。计算时间并不是真正的问题。

2 个答案:

答案 0 :(得分:0)

您可以使用product的块形式(请参见https://apidock.com/ruby/Array/product)。

例如,以下内容使我的红宝石处理无法响应,因此我必须kill -9

list = Array.new(100_000)
list.product list

不过,我可以control+c使用阻止形式随时将其停止:

list.product(list) do |combination|
  puts combination.join(",")
end

答案 1 :(得分:0)

让我开始计算两个数组乘积的问题。稍后,我将解决更普遍的问题。

arr1 = [1,2,3]
arr2 = [4,5,6]

arr1.product(arr2)
  #=> [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5],
  #    [2, 6], [3, 4], [3, 5], [3, 6]]

要减少内存需求(即避免构造上述数组),可以构造一个枚举器。

product_enum = Enumerator.new do |y|
  arr1.size.times do |i|
    e1 = arr1[i]
    arr2.size.times { |j| y << [e1, arr2[j]] }
  end
end
  #=> #<Enumerator: 3<Enumerator::Generator:0x00...20>:each>

loop do
  p product_enum.next
end
[1, 4]
[1, 5]
[1, 6]
[2, 4]
[2, 5]
[2, 6]
[3, 4]
[3, 5]
[3, 6]    

要重用枚举器,请执行product_enum.rewind。请参见Enumerator::newEnumerator#rewindEnumerator::new的块变量y被称为 yielder 。枚举器可以被认为是根据某些规则生成值的机器。

Ruby的方法Array#combinationArray#permutation和其他方法返回枚举数,但是由于我不知道的原因,Array#product返回一个数组。 (也许读者可以在评论中解释原因。)

我们可以构造一个Array方法product_enum,该方法返回一个枚举器,您可以将其用于解决问题。

class Array
  def product_enum(*arr)
    Enumerator.new do |y|
      self.size.times do |i|
        e1 = self[i]
        product_enum_recurse(y,[e1],*arr)
      end
    end
  end

  def product_enum_recurse(y,a,*arr)
    v, *rest = arr
    v.size.times do |i|
      if rest.empty?
        y << [*a, v[i]]
      else
        product_enum_recurse(y,[*a, v[i]],*rest)
      end
    end
  end
end 

e = [1,2].product_enum([3,4], [5,6], [7,8])
  #=> #<Enumerator: #<Enumerator::Generator:0x00...0>:each> 

loop do
  p e.next
end
[1, 3, 5, 7]
[1, 3, 5, 8]
[1, 3, 6, 7]
[1, 3, 6, 8]
[1, 4, 5, 7]
[1, 4, 5, 8]
[1, 4, 6, 7]
[1, 4, 6, 8]
[2, 3, 5, 7]
[2, 3, 5, 8]
[2, 3, 6, 7]
[2, 3, 6, 8]
[2, 4, 5, 7]
[2, 4, 5, 8]
[2, 4, 6, 7]
[2, 4, 6, 8]