2017-11-30 29 views
7

配列arrのすべての要素が等しいかどうかをテストするための最短方法はall(arr[1] .== arr)です。これは確かに短いですが、それはちょっと面白くないようです。これを行う組み込み関数はありますか?Julia配列のすべての要素が等しいかどうか確認してください

==(arr...)の行に何かがあると思われますが、==オペレータは2つの引数しか取れないため、動作しません。私はジュリアがarr[1] == arr[2] == arr[3]のような式をどのように構文解析するのか分かりませんが、任意の数の要素を持つ配列にこれを適合させる方法はありますか?

答えて

7

偉大な質問@tparkerと偉大な答え@コリンボワーズ。両者について考えようとしているうちに、ストレートな古い学校のジュリアンの方法 - for -loopを試してみました。同じ要素の長いベクトルの重要な入力の結果が速かったので、このメモを追加します。また、関数名allequalは言及するのに十分なようです。

allequal_1(x) = all(y->y==x[1],x) 

allequal_2(x) = foldl(==,x) # another way but doesn't short-circuit :(

@inline function allequal_3(x) 
    length(x) < 2 && return true 
    e1 = x[1] 
    i = 2 
    @inbounds for i=2:length(x) 
     x[i] == e1 || return false 
    end 
    return true 
end 

ベンチマーク:だからここ変異体である

julia> using BenchmarkTools 

julia> v = fill(1,10_000_000); # long vector of 1s 

julia> allequal_1(v) 
true 

julia> allequal_2(v) 
true 

julia> allequal_3(v) 
true 

julia> @btime allequal_1($v); 
    9.573 ms (1 allocation: 16 bytes) 

julia> @btime allequal_2($v); 
    10.585 ms (0 allocations: 0 bytes) 

julia> @btime allequal_3($v); 
    6.853 ms (0 allocations: 0 bytes) 

UPDATE:ショート機会があるときに、ベンチマークにもう一つの重要な場合があります。だから、(commmentに要求されるように):

julia> v[100] = 2 
2 

julia> allequal_1(v),allequal_2(v),allequal_3(v) 
(false, false, false) 

julia> @btime allequal_1($v); 
    108.946 ns (1 allocation: 16 bytes) 

julia> @btime allequal_2($v); 
    10.325 ms (0 allocations: 0 bytes) 

julia> @btime allequal_3($v); 
    68.221 ns (0 allocations: 0 bytes) 

allequal_2運賃ひどく、それがショートしないように、第2のバージョンは。

すべて等しいことは、forのバージョンがベースでallequalになるはずです。

+0

ありがとう、 'foldl'はまさに私が探していた機能です。第三の実装がなぜより速いのかについての '@ inbounds'を超える理由があると思いますか? – tparker

+0

Julia Baseは、一般的な機能を組み合わせて構築し、多くの実装を高水準かつ抽象的にすることによって、Julia Baseがどのように構築されるのが本当に好きです。 'reduce.jl'を見てください。私は 'allequal'(それは実際にBaseにあるべきではありませんが)は、ハイパー最適化されたカスタムループを作るのではなく、' all'のようないくつかの一般的なコンストラクトの上に構築されたものです。このようにして、 'all'のパフォーマンスを向上させることで、それに依存するすべての機能が向上します。 – DNF

+0

BTW。 'foldl'は短絡していませんので、ここでは間違いなく最適です。 – DNF

9

allは適切なソリューションですが、それが短絡行動を(発見されたfalseとすぐ壊れる)を採用しますので、あなたは、述語pおよび反復可能itr方法all(p, itr)をしたいです。だから、:

all(y->y==x[1], x) 

違いを表示するには、次の少しスピードテストを実行できます。

for n = 100000:250000:1100000 
    x = rand(1:2, n); 
    @time all(x .== x[1]); 
    @time all(y->y==x[1], x); 
    println("------------------------") 
end 

時間をコンパイルタイミングであるとして最初の反復を無視します。第二は、(1)(itr用データ生成処理に応じて)最悪で最高のO(N)でOである

0.000177 seconds (22 allocations: 17.266 KiB) 
    0.006155 seconds (976 allocations: 55.062 KiB) 
------------------------ 
    0.000531 seconds (23 allocations: 47.719 KiB) 
    0.000003 seconds (1 allocation: 16 bytes) 
------------------------ 
    0.000872 seconds (23 allocations: 78.219 KiB) 
    0.000001 seconds (1 allocation: 16 bytes) 
------------------------ 
    0.001210 seconds (23 allocations: 108.781 KiB) 
    0.000001 seconds (1 allocation: 16 bytes) 
------------------------ 
    0.001538 seconds (23 allocations: 139.281 KiB) 
    0.000002 seconds (1 allocation: 16 bytes) 

第1の解決策は、かなり明らかにO(N)です。

+0

うわー、素晴らしい答え!最初の要素と手動で比較するよりコンパクトな構文はないことに私は驚いています。 – tparker

+2

@tparker 'allsame(x)= all(y-> y == x [1]、x)'です。言語に関する素晴らしい点の1つは、これらのショートカットを自分で作成して、自分のコアパッケージに入れておくことがどれほど簡単かということです。より一般的には、ジュリアコミュニティには、ベースを可能な限りトリムに保つよう強い要請があります。すべてのタイプの操作のための素敵でシンプルな関数名はパッケージに入れることができ、それが好きな人がいればパッケージは一般的になります。 –

+0

これは、空の配列の中でも異なる要素がないので、空の配列に対しても機能します: 'allsame([])== true'です。 – mschauer

関連する問題