2012-01-16 2 views
11

Учитывая два списка A={a1,a2,a3,...an} и B={b1,b2,b3,...bn}, я бы сказал A>=B, если и только если все ai>=bi.Есть ли эффективный простой способ сравнить два списка с одинаковой длиной с Mathematica?

Существует встроенное логическое сравнение двух списков, A==B, но нет A>B. нам нужно сравнить каждый элемент, как этот

[email protected]@Table[A[[i]]>=B[[i]],{i,n}]

Есть лучшие трюки, чтобы сделать это делать?

EDIT: Большое спасибо за всех вас.

Вот еще один вопрос:

Как найти максимальный список (если есть) из N списков?

Any efficient easy way to find the maximum list among N lists with the same length using Mathematica?

ответ

18

Способ 1: Я предпочитаю этот метод.

NonNegative[Min[a - b]] 

Метод 2: Это просто для удовольствия. Как отметил Леонид, для данных, которые я использовал, дается немного несправедливое преимущество. Если один делает попарных сравнений, и возвращать значение False и перерыв, когда это необходимо, то цикл может быть более эффективным (хотя я обычно избегают петель в ММА):

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


Редактировать: я удалил тайминги для своего метода №2, поскольку они могут вводить в заблуждение. И метод № 1 более подходит в качестве общего подхода.

+2

Ницца. Быстро и просто. +1 –

+0

Спасибо, Леонид. – DavidC

+0

Обратите внимание, что поскольку вы генерируете случайные списки, ваш последний метод (в котором есть странные жестко закодированные константы, но я предполагаю, что это остатки из вашего кода разработки. В любом случае, они делают это неправильно здесь, но давайте не будем это игнорировать) получает несправедливое преимущество, поскольку статистически нарушение условий произойдет очень рано в цикле. Вы также должны проверить списки, в которых результат будет «True», и в этом случае списки верхнего уровня гарантированно будут медленнее. Фактически, ваш последний метод - это вариант верхнего уровня скомпилированного кода @acl. –

8

Например,

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 раз уродливый тоже.

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

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

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

EDIT 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.

+1

+1. Вы могли бы сделать это немного проще: 'cmp1 = Compile [{{a, _Integer, 1}, {b, _Integer, 1}}, Do [Если [a [[p]] "C"] '. –

+0

Компиляция явно быстрее. Тем не менее, я удивлен тем, что мой метод № 1 так же хорошо, как и он. – DavidC

+0

@ David, возможно, так быстро, потому что 'Developer'PackedArrayQ [a-b]' возвращает 'True' – acl

4

Поскольку вы упомянули эффективность в качестве фактора в вашем вопросе, вы можете найти эти функции полезны:

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.

+0

Ваши функции более чем в 4 раза медленнее, чем у меня, согласно моим результатам. – DavidC

+0

@ Давид, я думаю, это зависит от данных. В моих тестах у меня была разница в 2 раза. Но да, они медленнее (по крайней мере, ваша первая функция). Я все равно буду держать этот ответ, потому что он иллюстрирует, как использовать 'SparseArray', но я согласен с тем, что ваше решение превосходит. –

+0

Я упомянул о временном соотношении, основанном на методе № 1 (поскольку метод №2 действительно просто для удовольствия). – DavidC

3

Функции сравнения, такие как 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] 

со списками с symoblic записей

[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.

2

При работе с упакованными массивами и цифровым компаратором, таким как >=, было бы сложно обыграть метод Дэвида №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]], , [email protected]], {i, [email protected]}] 
1 >= 1 
2 >= 3 

False 
+0

На самом деле это совсем не сложно, в моем ответе есть метод, который примерно на порядок быстрее ... (хороший совет на 'Inner', я не знал об эффективности, +1) – acl

+0

@acl Я не знаю 't подсчет компиляции C. ;-) (я знаю, что я, вероятно, должен преодолеть это, но между отсутствием его в v7 и необходимостью писать в этом ужасном процедурном стиле я просто не готов принять это как * Mathematica *.) Спасибо за голосование. –

+0

Почему вы говорите: «Если массивы упакованы ...», то петля - хороший выбор? Если я распакую 'a' и задаю' b = a', версия распакованного списка немного медленнее, чем упакованная. т.е. упаковка действительно не влияет на «Do» и производительность компании (хотя они, похоже, не распаковываются). Или я не понимаю? – acl

 Смежные вопросы

  • Нет связанных вопросов^_^