Есть ли эффективный простой способ сравнить два списка с одинаковой длиной с Mathematica?
Учитывая два списка A={a1,a2,a3,...an}
и B={b1,b2,b3,...bn}
, я бы сказал A>=B
тогда и только тогда, когда все ai>=bi
.
Существует встроенное логическое сравнение двух списков, A==B
, но не A>B
.
Нужно ли сравнивать каждый элемент следующим образом:
[email protected]@Table[A[[i]]>=B[[i]],{i,n}]
Какие-нибудь лучшие трюки?
EDIT:
Большое спасибо за всех вас.
Вот еще один вопрос:
Как найти Максимальный список (если существует) среди N списков?
Любой эффективный простой способ найти максимальный список среди N списков с одинаковой длиной, используя Mathematica?
Ответы
Ответ 1
Метод 1. Я предпочитаю этот метод.
NonNegative[Min[a - b]]
Метод 2. Это просто весело. Как отметил Леонид, для данных, которые я использовал, дается немного несправедливое преимущество.
Если вы делаете попарные сравнения и возвращаете False и Break, когда это необходимо,
то цикл может быть более эффективным (хотя я вообще избегаю циклов в ммa):
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 method *)
And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
(* acl uncompiled method *)
And @@ Thread[a >= b] // Timing
(* Leonid method *)
lessEqual[a, b] // Timing
(* David method #1 *)
NonNegative[Min[a - b]] // Timing
![timings 2]()
Изменить: я удалил тайминги для своего метода №2, поскольку они могут вводить в заблуждение. И метод № 1 более подходит в качестве общего подхода.
Ответ 2
Например,
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, [email protected]}];
flag],
CompilationTarget \[Rule] "C"
]
в 20 раз быстрее. 20 раз уродливый тоже.
ИЗМЕНИТЬ 2: Поскольку у Дэвида нет компилятора C, вот все результаты синхронизации с двумя отличиями. Во-первых, его второй метод был исправлен для сравнения всех элементов. Во-вторых, я сравниваю a
с самим собой, что является наихудшим (в противном случае мой второй метод выше должен будет сравнивать элементы до первого, чтобы нарушить условие).
(*OP method*)
And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
(*acl uncompiled method*)
And @@ Thread[a >= b] // Timing
(*Leonid method*)
lessEqual[a, b] // Timing
(*David method #1*)
NonNegative[Min[a - b]] // Timing
(*David method #2*)
Timing[result = True;
n = 1; While[n < Length[a],
If[a[[n]] < b[[n]], result = False; Break[]];
n++]; result]
(*acl compiled method*)
cmp[a, a] // Timing
![enter image description here]()
Таким образом, скомпилированный метод выполняется намного быстрее (обратите внимание, что второй метод David и скомпилированный метод здесь - это один и тот же алгоритм, и единственное различие - накладные расходы).
Все это зависит от мощности батареи, поэтому могут быть некоторые случайные колебания, но я думаю, что они являются репрезентативными.
РЕДАКТИРОВАТЬ 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, [email protected]}];
flag], CompilationTarget -> "C"]
то cmp2
в два раза быстрее, чем cmp
.
Ответ 3
Поскольку вы упомянули эффективность как фактор в своем вопросе, вы можете найти эти функции полезными:
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
.
Ответ 4
Функции сравнения, такие как Greater, GreaterEqual, Equal, Less, LessEqual
, могут быть применены к спискам несколькими способами (все они являются вариациями подхода в вашем вопросе).
С двумя списками:
a={a1,a2,a3};
b={b1,b2,b3};
и два экземпляра с числовыми записями
na={2,3,4}; nb={1,3,2};
вы можете использовать
[email protected]@NonNegative[na-nb]
Со списками с символическими элементами
[email protected]@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}]
Используя
listCompare[GreaterEqual,na,nb]
дает True
. С символическими записями
listCompare[GreaterEqual,a,b]
дает логически эквивалентное выражение a1 <= b1 && a2 <= b2 && a3 <= b3
.
Ответ 5
При работе с упакованными массивами и числовым компаратором, например >=
, было бы сложно выполнить метод David # 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
высок, является хорошим вариантом, например, метод David # 2. Это может быть лучше написано:
Null === Do[If[a[[i]] ~test~ b[[i]], , [email protected]], {i, [email protected]}]
1 >= 1
2 >= 3
False