Есть ли эффективный простой способ сравнить два списка с одинаковой длиной с Mathematica?
даны два списка A={a1,a2,a3,...an}
и B={b1,b2,b3,...bn}
, Я бы сказал A>=B
если и только если все ai>=bi
.
существует встроенное логическое сравнение двух списков,A==B
, а не A>B
.
Нужно ли сравнивать каждый такой элемент
And@@Table[A[[i]]>=B[[i]],{i,n}]
есть ли лучшие трюки, чтобы сделать это?
EDIT: Большое спасибо за всех вас.
вот еще один вопрос:
как найти максимальный список (если он существует) среди N списки?
5 ответов
Способ 1: Я предпочитаю этот метод.
NonNegative[Min[a - b]]
Способ 2: Это просто для удовольствия. Как отметил Леонид, это дает немного несправедливое преимущество для данных, которые я использовал. Если вы делаете попарные сравнения и возвращаете False и Break, когда это необходимо, тогда цикл может быть более эффективным (хотя я обычно избегаю циклов в mma):
result = True;
n = 1; While[n < 1001, If[a[[n]] < b[[n]], result = False; Break[]]; n++]; result
некоторые сравнения времени в списках 10^6 цифры:
a = Table[RandomInteger[100], {10^6}];
b = Table[RandomInteger[100], {10^6}];
(* OP's method *)
And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
(* acl's uncompiled method *)
And @@ Thread[a >= b] // Timing
(* Leonid's method *)
lessEqual[a, b] // Timing
(* David's method #1 *)
NonNegative[Min[a - b]] // Timing
Edit: я удалил тайминги для моего метода #2, так как они могут вводить в заблуждение. И метод №1 более подходит в качестве общего подхода.
например,
And @@ Thread[A >= B]
должен делать свою работу.
EDIT: с другой стороны, это
cmp = Compile[
{
{a, _Integer, 1},
{b, _Integer, 1}
},
Module[
{flag = True},
Do[
If[Not[a[[p]] >= b[[p]]], flag = False; Break[]],
{p, 1, Length@a}];
flag],
CompilationTarget \[Rule] "C"
]
в 20 раз быстрее. И в 20 раз уродливее.
EDIT 2: поскольку у Дэвида нет компилятора C, вот все результаты синхронизации с двумя различиями. Во-первых, его второй метод был зафиксирован для сравнения всех элементов. Во-вторых, я сравниваю a
к себе, что в худшем случае (в противном случае мой второй метод выше будет только нужно сравнить элементы до первого, чтобы нарушить условие).
(*OP's method*)
And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
(*acl's uncompiled method*)
And @@ Thread[a >= b] // Timing
(*Leonid's method*)
lessEqual[a, b] // Timing
(*David's method #1*)
NonNegative[Min[a - b]] // Timing
(*David's method #2*)
Timing[result = True;
n = 1; While[n < Length[a],
If[a[[n]] < b[[n]], result = False; Break[]];
n++]; result]
(*acl's compiled method*)
cmp[a, a] // Timing
таким образом, скомпилированный метод намного быстрее (обратите внимание, что второй метод Дэвида и скомпилированный метод здесь-тот же алгоритм, и единственная разница-накладные расходы).
все это от аккумулятора, поэтому могут быть некоторые случайные колебания, но я думаю, что они являются репрезентативными.
редактировать 3: Если, как ruebenko предположил в комментарии, я заменю Part
С Compile`GetElement
, такой
cmp2 = Compile[{{a, _Integer, 1}, {b, _Integer, 1}},
Module[{flag = True},
Do[If[Not[Compile`GetElement[a, p] >= Compile`GetElement[b, p]],
flag = False; Break[]], {p, 1, Length@a}];
flag], CompilationTarget -> "C"]
затем cmp2
- это в два раза быстрее cmp
.
поскольку вы упомянули эффективность как фактор в своем вопросе, вы можете найти эти функции полезными:
ClearAll[lessEqual, greaterEqual];
lessEqual[lst1_, lst2_] :=
SparseArray[1 - UnitStep[lst2 - lst1]]["NonzeroPositions"] === {};
greaterEqual[lst1_, lst2_] :=
SparseArray[1 - UnitStep[lst1 - lst2]]["NonzeroPositions"] === {};
эти функции будут разумно эффективны. Решение @David по-прежнему в два-четыре раза быстрее, и если вы хотите экстремальную скорость, и ваши списки численны (сделаны из целых или вещественных чисел), вы, вероятно, должны использовать компиляцию в C (решение @acl и аналогично для других операторов).
вы можете использовать те же методы (с помощью Unitize
вместо UnitStep
для реализации equal
и unequal
), чтобы реализовать другие операторы сравнения (>
, <
, ==
, !=
). Имейте в виду, что UnitStep[0]==1
.
функции сравнения, такие как Greater, GreaterEqual, Equal, Less, LessEqual
можно применить к спискам несколькими способами (все они являются вариантами подхода в вашем вопросе).
С двумя списками:
a={a1,a2,a3};
b={b1,b2,b3};
и два экземпляра с цифровой записи
na={2,3,4}; nb={1,3,2};
можно использовать
And@@NonNegative[na-nb]
списки с записями symoblic
And@@NonNegative[na-nb]
дает
NonNegative[a1 - b1] && NonNegative[a2 - b2] && NonNegative[a3 - b3]
для общих сравнений можно создать общую функцию сравнения как
listCompare[comp_ (_Greater | _GreaterEqual | _Equal | _Less | _LessEqual),
list1_List, list2_List] := And @@ MapThread[comp, {list1, list2}]
использование as
listCompare[GreaterEqual,na,nb]
дает True
. С символическими записями
listCompare[GreaterEqual,a,b]
дает logially эквивалентное выражение a1 <= b1 && a2 <= b2 && a3 <= b3
.
при работе с упакованными массивами и цифровой компаратор, таких как >=
было бы трудно превзойти метод Дэвида №1.
однако для более сложных тестов, которые не могут быть преобразованы в простую арифметику, требуется другой метод.
хорошим общим методом, особенно для распакованных списков, является использование Inner
:
Inner[test, a, b, And]
это не делает все сравнения раньше времени и поэтому значительно более эффективным в некоторых случаях, чем, например, And @@ MapThread[test, {a, b}]
. Это иллюстрирует разницу:
test = (Print[#, " >= ", #2]; # >= #2) &;
{a, b} = {{1, 2, 3, 4, 5}, {1, 3, 3, 4, 5}};
Inner[test, a, b, And]
1 >= 1 2 >= 3 False
And @@ MapThread[test, {a, b}]
1 >= 1 2 >= 3 3 >= 3 4 >= 4 5 >= 5 False
если массивы упакованы и особенно, если вероятность того, что возвращение False
высок, тогда цикл, такой как метод Дэвида #2, является хорошим вариантом. Может быть, лучше написать:
Null === Do[If[a[[i]] ~test~ b[[i]], , Return@False], {i, Length@a}]
1 >= 1 2 >= 3 False