Есть ли эффективный простой способ сравнить два списка с одинаковой длиной с 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 списки?

любой эффективный простой способ найти максимальный список среди N списков с одинаковой длиной с помощью Mathematica?

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

timings 2


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

enter image description here

таким образом, скомпилированный метод намного быстрее (обратите внимание, что второй метод Дэвида и скомпилированный метод здесь-тот же алгоритм, и единственная разница-накладные расходы).

все это от аккумулятора, поэтому могут быть некоторые случайные колебания, но я думаю, что они являются репрезентативными.

редактировать 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