Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays Ruby可能的数组值组合-性能_Arrays_Ruby_Combinations - Fatal编程技术网

Arrays Ruby可能的数组值组合-性能

Arrays Ruby可能的数组值组合-性能,arrays,ruby,combinations,Arrays,Ruby,Combinations,我需要根据条件快速确定数组中元素的可能uniq组合 它们具有以下结构: [[id,parent_id]] 我对较小的阵列没有问题。如果所有父_id都是uniq。例如: a = (1..6).to_a.map{ |a| [a,a] } => [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6]] a.combination(3).size # =>

我需要根据条件快速确定数组中元素的可能uniq组合

它们具有以下结构:

[[id,parent_id]]
我对较小的阵列没有问题。如果所有父_id都是uniq。例如:

a = (1..6).to_a.map{ |a| [a,a] }                                                
=> [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6]]
a.combination(3).size # => 20
马上回答

如果我有重复出现的父类ID,我仍然可以使用组合并遍历所有组合

a = (1..7).to_a.map{ |a| [a,a] };a[6] = [7,6]
=> [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 6]]
a.combination(3).size # => 35
valid_combos = a.combination(3).to_a.select { |c| c.map(&:last).uniq.size == c.size }.size # => 30
这在小型阵列上仍然很快。但是如果数组有33个条目,其中有1个重复出现的父id,那么我必须检查116683110个组合。这太慢了。当然

欢迎提供有关如何快速有效解决此问题的任何想法或提示

我喜欢数组类的组合方法。但我也会使用散列或集合

也可以有如下数组:

 a = [[1, 1], [2, 1], [3, 1], [4, 2], [5, 2], [6, 2], [7, 3], [8, 3]]
 a.combination(3).size #=> 56
但只有18个是“有效的”

感谢您的帮助

编辑:

有效输入无重复出现的父\u ID:

[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6,5]]
每个组合4个(5个uniq组合)的有效输出:

有效输入1重复出现的父\u ID:

[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6,5]]
每个组合4个(9个uniq组合)的有效输出:

这些是无效的组合[5,5]和[6,5]是不允许的:

[[[1, 1], [2, 2], [5, 5], [6, 5]], [[1, 1], [3, 3], [5, 5], [6, 5]], [[1, 1], [4, 4], [5, 5], [6, 5]], [[2, 2], [3, 3], [5, 5], [6, 5]], [[2, 2], [4, 4], [5, 5], [6, 5]], [[3, 3], [4, 4], [5, 5], [6, 5]]]

如果我理解正确,您希望所有可能的id组合都不共享父id。我尝试了一些不同的东西,只是为了好玩,不知道性能是否会提高

x = [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6,5]]
首先,让我们减少它

hash = x.reduce({}) {|hash, pair| (hash[pair.last] ||= []).push pair.first}
#=> {1=>[1], 2=>[2], 3=>[3], 4=>[4], 5=>[5, 6]}
现在我们得到所有可能的父ID组合

parents = hash.keys.combination(4).to_a
#=> [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]]
现在我们将每个父ID映射到它的子ID

children = parents.map do |array|
  array.map {|parent| hash[parent]}
end
#=>  [[[1], [2], [3], [4]], [[1], [2], [3], [5, 6]], [[1], [2], [4], [5, 6]], [[1], [3], [4], [5, 6]], [[2], [3], [4], [5, 6]]]
我们现在在齐膝深的阵列中。现在,我们取每个子数组的乘积来得到所有可能的组合,我们甚至不需要对它们进行uniq

children.map {|array| array.first.product *array.drop(1)}.flatten(1)
#=> [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 3, 4, 5], [1, 3, 4, 6], [2, 3, 4, 5], [2, 3, 4, 6]]
现在您有了所有ID的组合,如果仍然需要父ID,可以使用它们来查找父ID,使用
哈希表的相反部分

性能如何?我以跑步作为基准

50个条目,25个重复条目,4个组合:

3957124
Original:   8.719000   0.110000   8.829000 (  8.860909)
3957124
Simons:     4.875000   0.094000   4.969000 (  6.458309)
9811174
Original:  22.875000   0.281000  23.156000 ( 23.213483)
9811174
Simons:    20.703000   0.391000  21.094000 ( 21.232167)
所以从理论上看它看起来更快。但是,有125个条目,25个重复,4个组合:

3957124
Original:   8.719000   0.110000   8.829000 (  8.860909)
3957124
Simons:     4.875000   0.094000   4.969000 (  6.458309)
9811174
Original:  22.875000   0.281000  23.156000 ( 23.213483)
9811174
Simons:    20.703000   0.391000  21.094000 ( 21.232167)
这并不快。这是因为对于如此多的组合,Ruby将大部分时间花在内存分配上(尝试在Task Manager或
top
中查看),而Ruby的内存分配速度非常慢。实际上没有任何有效的方法预先分配内存,因此超过某一点,您就处于硬限制

但这只是因为您强制Ruby同时收集所有数组项。如果您的特定用例允许您单独处理每个组合,那么您可以避免大部分内存分配。通过对每个子数组()调用
yield


快多了。您还将观察到内存使用保持不变。然而,如果您有多个核心,原则上您可以并行,因为一旦您有了散列,每个组合就可以独立于其他组合工作。我将留给您尝试:)

如果我理解正确,您希望所有可能的id组合都不共享父id。我尝试了一些不同的东西,只是为了好玩,不知道性能是否会提高

x = [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6,5]]
首先,让我们减少它

hash = x.reduce({}) {|hash, pair| (hash[pair.last] ||= []).push pair.first}
#=> {1=>[1], 2=>[2], 3=>[3], 4=>[4], 5=>[5, 6]}
现在我们得到所有可能的父ID组合

parents = hash.keys.combination(4).to_a
#=> [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]]
现在我们将每个父ID映射到它的子ID

children = parents.map do |array|
  array.map {|parent| hash[parent]}
end
#=>  [[[1], [2], [3], [4]], [[1], [2], [3], [5, 6]], [[1], [2], [4], [5, 6]], [[1], [3], [4], [5, 6]], [[2], [3], [4], [5, 6]]]
我们现在在齐膝深的阵列中。现在,我们取每个子数组的乘积来得到所有可能的组合,我们甚至不需要对它们进行uniq

children.map {|array| array.first.product *array.drop(1)}.flatten(1)
#=> [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 3, 4, 5], [1, 3, 4, 6], [2, 3, 4, 5], [2, 3, 4, 6]]
现在您有了所有ID的组合,如果仍然需要父ID,可以使用它们来查找父ID,使用
哈希表的相反部分

性能如何?我以跑步作为基准

50个条目,25个重复条目,4个组合:

3957124
Original:   8.719000   0.110000   8.829000 (  8.860909)
3957124
Simons:     4.875000   0.094000   4.969000 (  6.458309)
9811174
Original:  22.875000   0.281000  23.156000 ( 23.213483)
9811174
Simons:    20.703000   0.391000  21.094000 ( 21.232167)
所以从理论上看它看起来更快。但是,有125个条目,25个重复,4个组合:

3957124
Original:   8.719000   0.110000   8.829000 (  8.860909)
3957124
Simons:     4.875000   0.094000   4.969000 (  6.458309)
9811174
Original:  22.875000   0.281000  23.156000 ( 23.213483)
9811174
Simons:    20.703000   0.391000  21.094000 ( 21.232167)
这并不快。这是因为对于如此多的组合,Ruby将大部分时间花在内存分配上(尝试在Task Manager或
top
中查看),而Ruby的内存分配速度非常慢。实际上没有任何有效的方法预先分配内存,因此超过某一点,您就处于硬限制

但这只是因为您强制Ruby同时收集所有数组项。如果您的特定用例允许您单独处理每个组合,那么您可以避免大部分内存分配。通过对每个子数组()调用
yield


快多了。您还将观察到内存使用保持不变。然而,如果您有多个核心,原则上您可以并行,因为一旦您有了散列,每个组合就可以独立于其他组合工作。我让你试试看:)

你可以这样做

代码

def combos(pairs, group_size)
  pairs.group_by(&:last).
        values.
        combination(group_size).
        flat_map { |a| a.shift.product(*a) }
end
示例

pairs = [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6,5]]

combos(pairs, 4)
   #=> [[[1, 1], [2, 2], [3, 3], [4, 4]],
   #    [[1, 1], [2, 2], [3, 3], [5, 5]],
   #    [[1, 1], [2, 2], [3, 3], [6, 5]],
   #    [[1, 1], [2, 2], [4, 4], [5, 5]],
   #    [[1, 1], [2, 2], [4, 4], [6, 5]],
   #    [[1, 1], [3, 3], [4, 4], [5, 5]],
   #    [[1, 1], [3, 3], [4, 4], [6, 5]],
   #    [[2, 2], [3, 3], [4, 4], [5, 5]],
   #    [[2, 2], [3, 3], [4, 4], [6, 5]]] 
 combos(pairs, 5)
   #=>  [[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
   #     [[1, 1], [2, 2], [3, 3], [4, 4], [6, 5]]] 

 combos(pairs, 1).size #=>  6 
 combos(pairs, 2).size #=> 14 
 combos(pairs, 3).size #=> 16 
 combos(pairs, 4).size #=>  9 
 combos(pairs, 5).size #=>  2
解释

对于示例中使用的数组
,以及

group_size = 4
我们执行以下计算。首先,我们按照每对的最后一个元素(即,
parent\u id
)对对的元素进行分组:

我们只需要此散列中的值:

b = h.values
  #=> [[[1, 1]], [[2, 2]], [[3, 3]], [[4, 4]], [[5, 5], [6, 5]]]
我们现在获得
b
元素的组合:

enum = b.combination(group_size)
  #=> b.combination(4)
  #=> #<Enumerator: [[[1, 1]], [[2, 2]], [[3, 3]], [[4, 4]],
  #                  [[5, 5], [6, 5]]]:combination(4)>
最后一步是将
enum
的每个元素映射到其元素的乘积(
enum
的每个元素都是成对的数组)。我们使用,因此随后不必进行任何展平:

enum.flat_map { |a| a.shift.product(*a) }
返回
组大小=4的示例中给出的数组

让我们更仔细地看看上一句话中发生了什么:

enum1 = enum.flat_map
  #=> #<Enumerator: #<Enumerator: [[[1, 1]], [[2, 2]], [[3, 3]], [[4, 4]],
  #                     [[5, 5], [6, 5]]]:combination(4)>:flat_map> 
第二次通过:

a = enum1.next
  #=> [[[1, 1]], [[2, 2]], [[3, 3]], [[5, 5], [6, 5]]]
我们将这四个阵列的乘积计算如下:

a[0].product(a[1], a[2], a[3])
  #=> [[[1, 1], [2, 2], [3, 3], [5, 5]],
  #    [[1, 1], [2, 2], [3, 3], [6, 5]]]
我们也可以这样写:

a[0].product(*a[1..-1])
或者,正如我所做的:

a.shift.product(*a)

请注意,在最后一个表达式中,
*a
a
是执行
a.shift
a
的剩余部分。

您可以按如下操作