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