Нахождение первого элемента списка Mathematica, превышающего порог - PullRequest
8 голосов
/ 09 сентября 2010

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

Я не очень хорошо знаю функцию манипулирования списками в Mathematica, возможно, кто-то может подсказать мне, как это сделать эффективно.

Ответы [ 6 ]

12 голосов
/ 09 сентября 2010

Select делает то, что вам нужно, и будет последовательным, соблюдая ранее существовавший порядок в списке:

Select[list, # > threshold &, 1]

Например:

In[1]:= Select[{3, 5, 4, 1}, # > 3 &, 1]

Out[1]= {5}

Вы можете указать любую пороговую или целевую функцию, которая вам нужна, во втором аргументе.

Третий аргумент указывает только один (то есть первый) элемент, который соответствует.

Надеюсь, что это поможет!

8 голосов
/ 15 августа 2011

Джо правильно заявляет в своем ответе , что можно ожидать, что метод двоичного поиска будет быстрее, чем Select, который, кажется, просто выполняет линейный поиск, даже если список отсортирован:

ClearAll[selectTiming]
selectTiming[length_, iterations_] := Module[
    {lst},
    lst = Sort[RandomInteger[{0, 100}, length]];
    (Do[Select[lst, # == 2 &, 1], {i, 1, iterations}] // Timing // 
     First)/iterations
  ]

enter image description here

(я произвольно установил порог 2 для демонстрационных целей).

Однако функция BinarySearch в Combinatorica a) не подходит (она возвращаетэлемент, который соответствует запрошенному, но не первый (самый левый), что задает вопрос.

Чтобы получить крайний левый элемент, который больше порога, с учетом упорядоченного списка, мы можемвыполните рекурсивно:

binSearch[lst_,threshold_]:= binSearchRec[lst,threshold,1,Length@lst]

(*
return position of leftmost element greater than threshold
breaks if the first element is greater than threshold
lst must be sorted
*)
binSearchRec[lst_,threshold_,min_,max_] :=
    Module[{i=Floor[(min+max)/2],element},
        element=lst[[i]];
        Which[
            min==max,max,
            element <= threshold,binSearchRec[lst,threshold,i+1,max],
            (element > threshold) && ( lst[[i-1]] <= threshold ), i,
            True, binSearchRec[lst,threshold,min,i-1]
        ]
    ]

или итеративно:

binSearchIterative[lst_,threshold_]:=Module[
    {min=1,max=Length@lst,i,element},
    While[
        min<=max,
        i=Floor[(min+max)/2];
        element=lst[[i]];
        Which[
            min==max, Break[],
            element<=threshold, min=i+1,
            (element>threshold) && (lst[[i-1]] <= threshold), Break[],
            True, max=i-1
        ]
    ];
    i
]

Рекурсивный подход понятнее, но я остановлюсь на итеративном.

Чтобы проверить его скорость,

ClearAll[binSearchTiming]
binSearchTiming[length_, iterations_] := Module[
    {lst},
    lst = Sort[RandomInteger[{0, 100}, length]];
    (Do[binSearchIterative[lst, 2], {i, 1, iterations}] // Timing // 
     First)/iterations
  ]

, который выдает

enter image description here

, поэтому намного быстрее и с лучшими характеристиками масштабирования.

На самом деле его не нужно компилироватьно я все равно.

В сВ таком случае, не используйте Select для длинных списков.

На этом мой ответ завершен.Далее следуют некоторые комментарии о выполнении бинарного поиска вручную или с помощью пакета Combinatorica.

Я сравнил скорость (скомпилированной) короткой процедуры для выполнения бинарного поиска с BinarySearch из Combinatorica.Обратите внимание, что это не делает то, что задает вопрос (и не делает BinarySearch из Combinatorica);код, который я привел выше, делает.

Бинарный поиск может быть реализован итеративно как

binarySearch = Compile[{{arg, _Integer}, {list, _Integer, 1}},
           Module[ {min = 1, max = Length@list,
                 i, x},
               While[
                    min <= max,
                    i = Floor[(min + max)/2];
                    x = list[[i]];
                    Which[
                         x == arg, min = max = i; Break[],
                         x < arg, min = i + 1,
                         True, max = i - 1
                     ]
                ];
               If[ 0 == max,
                   0,
                   max
               ]
           ], 
        CompilationTarget -> "C", 
        RuntimeOptions -> "Speed"
];

, и теперь мы можем сравнить это и BinarySearch с Combinatorica.Обратите внимание, что a) список должен быть отсортирован b) это не вернет первый соответствующий элемент, но a соответствующий элемент.

lst = Sort[RandomInteger[{0, 100}, 1000000]];

Давайте сравнимдве процедуры двоичного поиска.Повторяю 50000 раз:

Needs["Combinatorica`"]
Do[binarySearch[2, lst], {i, 50000}] // Timing
Do[BinarySearch[lst, 2], {i, 50000}] // Timing
(*
{0.073437, Null}
{4.8354, Null}
*)

Так что рукописный быстрее.Теперь, поскольку на самом деле бинарный поиск просто посещает 6-7 точек в списке для этих параметров (например, что-то вроде {500000, 250000, 125000, 62500, 31250, 15625, 23437}), очевидно, что разница просто накладная;возможно BinarySearch является более общим, например, или не скомпилировано.

5 голосов
/ 15 сентября 2010

Возможно, вы захотите взглянуть также на TakeWhile [] и LengthWhile [].

http://reference.wolfram.com/mathematica/ref/TakeWhile.html http://reference.wolfram.com/mathematica/ref/LengthWhile.html

3 голосов
/ 14 августа 2011
list /. {___, y_ /; y > 3, ___} :> {y}

Например

{3, 5, 4, 1} /. {___, y_ /; y > 3, ___} :> {y}

{5}
3 голосов
/ 14 августа 2011

Использование Select решит проблему, но это плохое решение, если вы заботитесь об эффективности.Select охватывает все элементы списка и, следовательно, будет занимать время, линейное по длине списка.

Поскольку вы говорите, что список упорядочен, гораздо лучше использовать BinarySearch, который будет работать во время, логарифмическое по размеру списка.Выражение ( edit : я внес небольшую корректировку, так как предыдущее написанное мной выражение не обрабатывало правильно повторяющиеся элементы в списке. еще одно редактирование : это по-прежнему не работает, когдаСам порог появляется в списке как повторяющийся элемент, см. комментарии):

Floor[BinarySearch[list,threshold]+1]

даст вам индекс нужного элемента.Если все элементы меньше порога, вы получите длину списка плюс один.
ps не забудьте позвонить на Needs["Combinatorica'"] перед использованием BinarySearch.

1 голос
/ 10 декабря 2016

Только для справки в будущем, начиная с v10 , вы можете использовать SelectFirst

Он имеет некоторые дополнительные тонкости, такие как возвращение Missing[] или значений по умолчанию.

Из документов:

SelectFirst[{e1,e2,…}, crit] дает первое ei, для которого crit[ei] равно True, или Missing["NotFound"], если ничего не найдено.

SelectFirst[{e1,e2,…}, crit, default] дает default, если нет ei, для которого crit[ei] равно True.

Для вашего конкретного случая вы должны использовать:

SelectFirst[list, # > threshold &]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...