t-nissieの日記: Ruby 2.4で追加されるArray#sumで配列の要素の総和が正確かつ高速になる 2
2016年(のクリスマス?)にリリースされるRuby 2.4、追加される
Array#sumで配列の要素の総和が正確かつ高速になるようです。
Array#sumはカハン (Kahan) の加算アルゴリズムを使うので、要素がFloatの場合
inject(:+)より正確で高速。injectのように初期値やブロックも引数にできる。便利そう。
Feature #12217: Introducing Enumerable#sum for precision compensated summation and revert r54237
結局Enumerable#sumでなくArray#sumになった。
しかし、Rubyは配列内の要素の型をちゃんぽんにできるから
array.cのrb_ary_sum()がスパゲッティ!
OCamlなら型は一意だから楽か。
Juliaのsum_kbnは(暗黙の型変換があるから?)
Juliaでそのまま書いてあるけどなんであんなに速いのか。
$ ./miniruby --version
ruby 2.4.0dev (2016-04-18) [x86_64-darwin15]
$ /usr/bin/time ./miniruby -e 'p Array.new(10_000_000,0.1).inject(:+)'
999999.9998389754
0.45 real 0.41 user 0.03 sys
$ /usr/bin/time ./miniruby -e 'p Array.new(10_000_000,0.1).sum'
1000000.0
0.12 real 0.08 user 0.03 sys
$ /usr/bin/time ./miniruby -e 'p Array.new(10_000_000,0.1).sum(999.0){|e| e**2}'
100999.00000000001
1.21 real 1.17 user 0.03 sys
$ /usr/bin/time ./miniruby -e 'p Array.new(10_000_000,0.1).sum(999.0){|e| e*e}'
100999.00000000001
0.74 real 0.70 user 0.03 sys
なお、Ruby 2.4のinjectは2.3のよりちょっと速くなってはいるみたい。
$ ruby --version
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
$ ruby -r benchmark -e 'Benchmark.bm(6){|r| r.report("inject"){Array.new(10_000_000,0.1).inject{:+}}}'
user system total real
inject 0.790000 0.020000 0.810000 ( 0.814554)
$ /usr/bin/time ruby -e 'p Array.new(10_000_000,0.1).inject(:+)'
999999.9998389754
0.78 real 0.73 user 0.04 sys
計時はすべて
Intel Core i5 (Ivy Bridge) 2.5 GHz on MacBook Pro (Retina, 13-inch, Late 2012)
で実行。
追記
Juliaで暗黙の型変換があるのは
配列に代入する時でした
using Base.Test
## Kahan ##
a = [0.1 for i=1:10_000_000]
a[10] = 1
println(a[10])
@test isa(a[10], Int64) == false
@test isa(a[11], Int64) == false
@test isa(a[10], Float64) == true
@test isa(a[11], Float64) == true
@test sum_kbn(a) == 1_000_000.9
追記2
Kahanのアルゴリズムは使われないけど、複素数の和も取れる。
文字列はつながらない。
$ ./miniruby -e 'p Array.new(10_000_000,0.1+0.1i).sum'
(999999.9998389754+999999.9998389754i)
$ ./miniruby -e 'p ["abc", "def"].sum'
-e:1:in `+': String can't be coerced into Fixnum (TypeError)
from -e:1:in `sum'
from -e:1:in `<main>'
普通に (スコア:0)
Double-Double型用意すればいいんちゃうの
Re:普通に (スコア:1)
カハンの加算アルゴリズムを使うのは
Double-Double演算を用意するよりお手軽で
拡張精度や四倍精度を使うよりポータブルだからではないでしょうか
love && peace && free_software
t-nissie