Posmax: как argmax, но дает положение (позиции) элемента x, для которого f [x] является максимальным
Mathematica имеет встроенную функцию ArgMax для функций над бесконечными доменами, основанную на стандартное математическое определение.
Аналог для конечных доменов является удобной функцией полезности.
Учитывая функцию и список (назовите это доменом функции), верните элемент списка, который максимизирует функцию.
Здесь приведен пример конечного argmax в действии:
Канонизировать имена команд НФЛ
И здесь моя реализация (наряду с argmin для хорошей меры):
(* argmax[f, domain] returns the element of domain for which f of
that element is maximal -- breaks ties in favor of first occurrence. *)
SetAttributes[{argmax, argmin}, HoldFirst];
argmax[f_, dom_List] := Fold[If[f[#1]>=f[#2], #1, #2]&, First[dom], Rest[dom]]
argmin[f_, dom_List] := argmax[-f[#]&, dom]
Во-первых, это самый эффективный способ реализации argmax?
Что делать, если вам нужен список всех максимальных элементов, а не только первый?
Во-вторых, как насчет связанной функции posmax, что вместо возврата максимального элемента (ов) возвращается позиция максимальных элементов?
Ответы
Ответ 1
@dreeves, вы правы в том, что Ordering
является ключом к самой быстрой реализации ArgMax над конечным доменом:
ArgMax[f_, dom_List] := dom[[Ordering[f /@ dom, -1]]]
Часть проблемы с вашей исходной реализацией с использованием Fold
заключается в том, что вы в конечном итоге оцениваете f
вдвое больше, чем необходимо, что неэффективно, особенно при медленном вычислении f
. Здесь мы оцениваем только f
один раз для каждого члена домена. Когда в домене много дублированных элементов, мы можем дополнительно оптимизировать memoizing значения f
:
ArgMax[f_, dom_List] :=
Module[{g},
g[e___] := g[e] = f[e]; (* memoize *)
dom[[Ordering[g /@ dom, -1]]]
]
Это было примерно на 30% быстрее в некоторых базовых тестах для списка из 100 000 случайных чисел от 0 до 100.
Для функции posmax
этот несколько не элегантный подход - это самая быстрая вещь, которую я могу придумать:
PosMax[f_, dom_List] :=
Module[{y = f/@dom},
[email protected][y, Max[y]]
]
Конечно, мы можем снова применить memoization:
PosMax[f_, dom_List] :=
Module[{g, y},
g[e___] := g[e] = f[e];
y = g /@ dom;
[email protected][y, Max[y]]
]
Чтобы получить все максимальные элементы, теперь вы можете просто реализовать ArgMax
в терминах posmax
:
ArgMax[f_, dom_List] := dom[[PosMax[f, dom]]]
Ответ 2
Для posmax вы можете сначала сопоставить функцию над списком, а затем просто запросить позицию максимального элемента (ов). То есть:
posmax[f_, dom_List] := posmax[f /@ dom]
где posmax[list]
определяется полиморфно, чтобы просто вернуть положение максимального элемента (ов).
Оказывается, есть встроенная функция Ordering, которая по существу делает это.
Таким образом, мы можем определить однопользовательскую версию posmax следующим образом:
posmax[dom_List] := Ordering[dom, -1][[1]]
Я только что протестировал это по сравнению с версией на основе цикла и рекурсивной версией, а Ordering - во много раз быстрее.
Рекурсивная версия довольно, поэтому я покажу ее здесь, но никогда не пытаюсь запускать ее на больших входах.
(* posmax0 is a helper function for posmax that returns a pair with the position
and value of the max element. n is an accumulator variable, in lisp-speak. *)
posmax0[{h_}, n_:0] := {n+1, h}
posmax0[{h_, t___}, n_:0] := With[{best = posmax0[{t}, n+1]},
If[h >= best[[2]], {n+1, h}, best]]
posmax[dom_List] := [email protected][dom, 0]
posmax[f_, dom_List] := [email protected][f /@ dom, 0]
posmax[_, {}] := 0
Ничто из этого не касается вопроса о том, как найти все максимальные элементы (или их позиции).
Это обычно не подходит для меня на практике, хотя я думаю, что было бы хорошо иметь.