Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if all the elements of a Julia array are equal

Tags:

The shortest way I can think of to test whether all the elements in an array arr are equal is all(arr[1] .== arr). While this is certainly short, it seems a bit inelegant. Is there a built-in function that does this?

I suspect there's something along the lines of ==(arr...), but that doesn't work because the == operator can only take two arguments. I'm not sure how Julia parses expressions like arr[1] == arr[2] == arr[3], but is there some way to adapt this to an array with an arbitrary number of elements?

like image 266
tparker Avatar asked Nov 30 '17 02:11

tparker


People also ask

How do you check if all elements in a matrix are equal?

To check if all values in an array are equal:Use the Array. every() method to iterate over the array. Check if each array element is equal to the first one. The every method only returns true if the condition is met for all array elements.

How do you know if two arrays are equal Julia?

Use isequal : Similar to == , except for the treatment of floating point numbers and of missing values. isequal treats all floating-point NaN values as equal to each other, treats -0.0 as unequal to 0.0 , and missing as equal to missing . Always returns a Bool value.


2 Answers

Great question @tparker and great answer @ColinTBowers. While trying to think about them both, it occurred to me to try the straight-forward old-school Julian way-of-the-for-loop. The result was faster on the important input of a long vector of identical elements, so I'm adding this note. Also, the function name allequal seems to be appropriate enough to mention. So here are the variants:

allequal_1(x) = all(y->y==x[1],x)  # allequal_2(x) used to be erroneously defined as foldl(==,x)     @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 

And the benchmark:

julia> using BenchmarkTools  julia> v = fill(1,10_000_000);  # long vector of 1s  julia> allequal_1(v) true  julia> allequal_3(v) true  julia> @btime allequal_1($v);   9.573 ms (1 allocation: 16 bytes)  julia> @btime allequal_3($v);   6.853 ms (0 allocations: 0 bytes) 

UPDATE: Another important case to benchmark is when there is a short-circuit opportunity. So (as requested in 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_3($v);   68.221 ns (0 allocations: 0 bytes) 

All things being equal, a for version should get to be allequal in Base.

like image 129
Dan Getz Avatar answered Oct 21 '22 05:10

Dan Getz


all is the right solution, but you want the method all(p, itr) for predicate p and iterable itr, since it will employ short-circuiting behaviour (break as soon as a false is found). So:

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

To see the difference, you can run the following little speed test:

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

Ignore the first iteration as it is timing compile time.

  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) 

The first solution is fairly obviously O(n), while the second is O(1) at best and O(n) at worst (depending on the data generating process for itr).

like image 44
Colin T Bowers Avatar answered Oct 21 '22 05:10

Colin T Bowers