@ dreeves, вы правы в том, что Ordering
является ключом к самой быстрой реализации ArgMax в конечном домене:
ArgMax[f_, dom_List] := dom[[Ordering[f /@ dom, -1]]]
Отчасти проблема с вашей первоначальной реализацией, использующей Fold
, заключается в том, что в итоге вы оцениваете f
в два раза больше, чем необходимо, что неэффективно, особенно когда вычисления f
идут медленно. Здесь мы оцениваем f
только один раз для каждого члена домена. Когда в домене много дублированных элементов, мы можем дополнительно оптимизировать, запоминая значения 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},
Flatten@Position[y, Max[y]]
]
Конечно, мы можем применить напоминание снова:
PosMax[f_, dom_List] :=
Module[{g, y},
g[e___] := g[e] = f[e];
y = g /@ dom;
Flatten@Position[y, Max[y]]
]
Чтобы получить всех максимальных элементов, теперь вы можете просто реализовать ArgMax
в терминах PosMax
:
ArgMax[f_, dom_List] := dom[[PosMax[f, dom]]]