Проверьте, равны ли все элементы массива Julia

Самый короткий способ, с помощью которого я могу проверить, равно ли все элементы в массиве arr равны all(arr[1] .== arr). Хотя это, конечно, коротко, кажется немного неэлегантным. Есть ли встроенная функция, которая делает это?

Я подозреваю, что есть что-то в строках ==(arr...), но это не работает, потому что оператор == может принимать только два аргумента. Я не уверен, как Джулия разбирает выражения типа arr[1] == arr[2] == arr[3], но есть ли способ адаптировать это к массиву с произвольным количеством элементов?

Ответ 1

Отличный вопрос @tparker и отличный ответ @ColinTBowers. Пытаясь задуматься о них обоих, мне пришло в голову попробовать прямолинейную старую школу Джулианского пути-the-the-t20. Результат был быстрее на важном вводе длинного вектора одинаковых элементов, поэтому я добавляю эту заметку. Кроме того, имя функции 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)

ОБНОВЛЕНИЕ: Еще один важный момент для тестирования - когда есть возможность короткого замыкания. Таким образом (по запросу в 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 плохо работает, так как она не замыкается.

При прочих равных условиях версия for должна быть allequal в базе.

Ответ 2

all - правильное решение, но вы хотите использовать метод all(p, itr) для предиката p и итерабельного itr, так как он будет использовать короткое замыкание (перерыв, как только будет найден false), Итак:

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

Игнорируйте первую итерацию, поскольку это время компиляции времени.

  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)

Первое решение довольно очевидно O (n), а второе - O (1) в лучшем случае и O (n) в худшем случае (в зависимости от процесса генерации данных для itr).

Ответ 3

Просто небольшое улучшение: allsame(x) = all(y → y == first(x), x) является более общим, чем allsame(x) = all(y → y == x[1], x), и работает, даже если x является чем-то отличным от AbstractArray, например, генератором.

Ответ 4

У меня недостаточно высокая репутация, чтобы комментировать ответ, предложивший foldl(==, x), но я не думаю, что он работает:

julia> foldl(==, [3,3,3])
false

julia> (3 == 3) == 3
false

julia> 3 == 3 == 3
true

julia> foldl(==, [3,3,1])
true

В приведенном выше примере, foldl(==, [3,3,3]) по существу (3 == 3) == 3, что упрощается до true == 3.