在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_a
中array 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) ------
太糟糕了。 🙁