パスワードを忘れた? アカウント作成
12755775 journal
Ruby

t-nissieの日記: Ruby 2.4で追加されるArray#sumで配列の要素の総和が正確かつ高速になる 2

日記 by t-nissie

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>'

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

人生の大半の問題はスルー力で解決する -- スルー力研究専門家

読み込み中...