在Ruby中乘以两个数组并获得乘积值之和的有效方法是什么?

在Ruby中乘以两个数组并获得乘法值之和的有效方法是什么? 我在Ruby中有两个数组:

array_A = [1, 2, 1, 4, 5, 3, 2, 6, 5, 8, 9] array_B = [3, 2, 4, 2, 5, 1, 3, 3, 7, 5, 4] 

我的目标是得到array_A * array_B的和值,即1 * 3 + 2 * 2 + 1 * 4 + … + 8 * 5 + 9 * 4。

因为我需要在我的应用程序中计算它们数百万次,所以进行此类计算的最有效方法是什么?

它就像一个矩阵计算:1 * N矩阵* N * 1矩阵或矢量点积。

更新

我刚刚根据新评论更新了基准测试。 按照约书亚的评论 ,注入方法将获得25%的加速, array walking without to_aarray walking without to_a

然而,由于速度是OP的主要目标,我们在比赛中获得了新的赢家,在我的基准测试中将运行时间从.34降低到.22

我仍然更喜欢inject方法,因为它更像ruby-ish,但如果速度很重要,那么while循环似乎就是这样。

新答案

您可以随时对所有这些答案进行基准测试,我是出于好奇而做到的:

 > ./matrix.rb Rehearsal -------------------------------------------------------------- matrix method 1.500000 0.000000 1.500000 ( 1.510685) array walking 0.470000 0.010000 0.480000 ( 0.475307) array walking without to_a 0.340000 0.000000 0.340000 ( 0.337244) array zip 0.590000 0.000000 0.590000 ( 0.594954) array zip 2 0.500000 0.000000 0.500000 ( 0.509500) while loop 0.220000 0.000000 0.220000 ( 0.219851) ----------------------------------------------------- total: 3.630000sec user system total real matrix method 1.500000 0.000000 1.500000 ( 1.501340) array walking 0.480000 0.000000 0.480000 ( 0.480052) array walking without to_a 0.340000 0.000000 0.340000 ( 0.338614) array zip 0.610000 0.010000 0.620000 ( 0.625805) array zip 2 0.510000 0.000000 0.510000 ( 0.506430) while loop 0.220000 0.000000 0.220000 ( 0.220873) 

简单的数组行走胜利,Matrix方法更糟糕,因为它包括对象实例化。 我认为,如果你想击败inject方法(在这里击败意味着一个数量级最快),你需要实现一个C扩展并将其绑定在你的ruby程序中。

这是我用过的剧本

 #!/usr/bin/env ruby require 'benchmark' require 'matrix' array_A = [1, 2, 1, 4, 5, 3, 2, 6, 5, 8, 9] array_B = [3, 2, 4, 2, 5, 1, 3, 3, 7, 5, 4] def matrix_method a1, a2 (Matrix.row_vector(a1) * Matrix.column_vector(a2)).element(0,0) end n = 100000 Benchmark.bmbm do |b| b.report('matrix method') { n.times { matrix_method(array_A, array_B) } } b.report('array walking') { n.times { (0...array_A.count).to_a.inject(0) {|r, i| r + array_A[i]*array_B[i]} } } b.report('array walking without to_a') { n.times { (0...array_A.count).inject(0) {|r, i| r + array_A[i]*array_B[i]} } } b.report('array zip') { n.times { array_A.zip(array_B).map{|i,j| i*j }.inject(:+) } } b.report('array zip 2') { n.times { array_A.zip(array_B).inject(0) {|r, (a, b)| r + (a * b)} } } b.report('while loop') do n.times do sum, i, size = 0, 0, array_A.size while i < size sum += array_A[i] * array_B[i] i += 1 end sum end end end 

穿过每个元素应该是必须的

 (0...array_A.count).inject(0) {|r, i| r + array_A[i]*array_B[i]} 

我会从简单开始并使用Ruby矩阵类:

 require 'matrix' a = Matrix.row_vector( [1, 2, 1, 4, 5, 3, 2, 6, 5, 8, 9]) b = Matrix.column_vector([3, 2, 4, 2, 5, 1, 3, 3, 7, 5, 4]) result= a * b puts result.element(0,0) 

如果结果太慢,那么使用完全相同的方法,但使用外部数学库。

我就是这样做的

 array_A.zip(array_B).map{|i,j| i*j }.inject(:+) 

这是另一种方式:

 array_A.zip(array_B).inject(0) {|r, (a, b)| r + (a * b)} 

由于速度是我们的主要标准,我将根据Peter的基准提交这种方法,因为它是最快的。

 sum, i, size = 0, 0, a1.size while i < size sum += a1[i] * a2[i] i += 1 end sum 

试试NMatrix gem。 它是一个数值计算库。 我认为它使用Octave和Matlab使用的相同的C和C ++库。

你可以像这样做矩阵乘法:

 require 'nmatrix' array_A = [1, 2, 1, 4, 5, 3, 2, 6, 5, 8, 9] array_B = [3, 2, 4, 2, 5, 1, 3, 3, 7, 5, 4] vec_a = array_A.to_nm([1,array_A.length]) # create an NMatrix vec_b = array_B.to_nm([1,array_B.length]) sum = vec_a.dot(vec_b.transpose) 

我不确定使用纯Ruby如何比较速度,但我想它会更快,特别是对于大型和稀疏矢量。

 array1.zip(array2).map{|x| x.inject(&:*)}.sum 

编辑:矢量不是最快的(Marc Bollinger完全正确)。

这是带vector和n次的修改代码:

 require 'benchmark' require 'matrix' array_A = [1, 2, 1, 4, 5, 3, 2, 6, 5, 8, 9] array_B = [3, 2, 4, 2, 5, 1, 3, 3, 7, 5, 4] vector_A = Vector[*array_A] vector_B = Vector[*array_B] def matrix_method a1, a2 (Matrix.row_vector(a1) * Matrix.column_vector(a2)).element(0,0) end def vector_method a1, a2 a1.inner_product(a2) end n = 100000 Benchmark.bmbm do |b| b.report('matrix method') { n.times { matrix_method(array_A, array_B) } } b.report('array walking') { n.times { (0...array_A.count).to_a.inject(0) {|r, i| r + array_A[i]*array_B[i]} } } b.report('array walking without to_a') { n.times { (0...array_A.count).inject(0) {|r, i| r + array_A[i]*array_B[i]} } } b.report('array zip') { n.times { array_A.zip(array_B).map{|i,j| i*j }.inject(:+) } } b.report('array zip 2') { n.times { array_A.zip(array_B).inject(0) {|r, (a, b)| r + (a * b)} } } b.report('while loop') do n.times do sum, i, size = 0, 0, array_A.size while i < size sum += array_A[i] * array_B[i] i += 1 end sum end end b.report('vector') { n.times { vector_method(vector_A, vector_B) } } end 

结果如下:

 Rehearsal -------------------------------------------------------------- matrix method 0.860000 0.010000 0.870000 ( 0.911755) array walking 0.290000 0.000000 0.290000 ( 0.294779) array walking without to_a 0.190000 0.000000 0.190000 ( 0.215780) array zip 0.420000 0.010000 0.430000 ( 0.441830) array zip 2 0.340000 0.000000 0.340000 ( 0.352058) while loop 0.080000 0.000000 0.080000 ( 0.085314) vector 0.310000 0.000000 0.310000 ( 0.325498) ----------------------------------------------------- total: 2.510000sec user system total real matrix method 0.870000 0.020000 0.890000 ( 0.952630) array walking 0.290000 0.000000 0.290000 ( 0.340443) array walking without to_a 0.220000 0.000000 0.220000 ( 0.240651) array zip 0.400000 0.010000 0.410000 ( 0.441829) array zip 2 0.330000 0.000000 0.330000 ( 0.359365) while loop 0.080000 0.000000 0.080000 ( 0.090099) vector 0.300000 0.010000 0.310000 ( 0.325903) ------ 

太糟糕了。 🙁