posmax:类似于 argmax,但给出了 f[x] 最大的元素 x 的位置

发布于 2024-08-29 09:02:32 字数 916 浏览 9 评论 0原文

Mathematica 有一个内置函数 ArgMax 用于无限域上的函数,基于标准数学定义

有限域的模拟是一个方便的效用函数。 给定一个函数和一个列表(称为函数的域),返回列表中使函数最大化的元素。 下面是有限 argmax 的实际应用示例: 规范化 NFL 球队名称

这是我的实现(以及 argmin ) measure):

(* 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 怎么样,它不返回最大元素,而是返回最大元素的位置?

Mathematica has a built-in function ArgMax for functions over infinite domains, based on the standard mathematical definition.

The analog for finite domains is a handy utility function.
Given a function and a list (call it the domain of the function), return the element(s) of the list that maximize the function.
Here's an example of finite argmax in action:
Canonicalize NFL team names

And here's my implementation of it (along with argmin for good measure):

(* 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]

First, is that the most efficient way to implement argmax?
What if you want the list of all maximal elements instead of just the first one?

Second, how about the related function posmax that, instead of returning the maximal element(s), returns the position(s) of the maximal elements?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

被你宠の有点坏 2024-09-05 09:02:32

@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]]]
  ]

在一些基本测试中,对于 0 到 100 之间的 100,000 个随机整数列表,这大约快了 30%。

对于 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]]
  ]

要获得所有最大元素,您现在可以根据PosMax<来实现ArgMax /代码>:

ArgMax[f_, dom_List] := dom[[PosMax[f, dom]]]

@dreeves, you're correct in that Ordering is the key to the fastest implementation of ArgMax over a finite domain:

ArgMax[f_, dom_List] := dom[[Ordering[f /@ dom, -1]]]

Part of the problem with your original implementation using Fold is that you end up evaluating f twice as much as necessary, which is inefficient, especially when computing f is slow. Here we only evaluate f once for each member of the domain. When the domain has many duplicated elements, we can further optimize by memoizing the values of f:

ArgMax[f_, dom_List] :=
  Module[{g},
    g[e___] := g[e] = f[e]; (* memoize *)
    dom[[Ordering[g /@ dom, -1]]]
  ]

This was about 30% faster in some basic tests for a list of 100,000 random integers between 0 and 100.

For a posmax function, this somewhat non-elegant approach is the fastest thing I can come up with:

PosMax[f_, dom_List] :=
  Module[{y = f/@dom},
    Flatten@Position[y, Max[y]]
  ]

Of course, we can apply memoization again:

PosMax[f_, dom_List] := 
  Module[{g, y},
    g[e___] := g[e] = f[e];
    y = g /@ dom;
    Flatten@Position[y, Max[y]]
  ]

To get all the maximal elements, you could now just implement ArgMax in terms of PosMax:

ArgMax[f_, dom_List] := dom[[PosMax[f, dom]]]
一抹苦笑 2024-09-05 09:02:32

对于 posmax,您可以首先将函数映射到列表上,然后仅询问最大元素的位置。即:

posmax[f_, dom_List] := posmax[f /@ dom]

其中 posmax[list] 被多态定义为仅返回最大元素的位置。
事实证明,有一个内置函数 Ordering 基本上可以完成此操作。
因此,我们可以像这样定义 posmax 的单参数版本:

posmax[dom_List] := Ordering[dom, -1][[1]]

我刚刚针对基于循环的版本和递归版本进行了测试,排序速度要快很多倍。
递归版本非常漂亮,所以我将在这里展示它,但永远不要尝试在大输入上运行它!

(* 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] := First@posmax0[dom, 0]
posmax[f_, dom_List] := First@posmax0[f /@ dom, 0]
posmax[_, {}] := 0

这些都没有解决如何找到所有最大元素(或它们的位置)的问题。
在实践中我通常不会遇到这种情况,尽管我认为有这种情况会很好。

For posmax, you can first map the function over the list and then just ask for the position of the maximal element(s). Ie:

posmax[f_, dom_List] := posmax[f /@ dom]

where posmax[list] is polymorphically defined to just return the position of the maximal element(s).
It turns out there's a built-in function, Ordering that essentially does this.
So we can define the single-argument version of posmax like this:

posmax[dom_List] := Ordering[dom, -1][[1]]

I just tested that against a loop-based version and a recursive version and Ordering is many times faster.
The recursive version is pretty so I'll show it off here, but don't ever try to run it on large inputs!

(* 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] := First@posmax0[dom, 0]
posmax[f_, dom_List] := First@posmax0[f /@ dom, 0]
posmax[_, {}] := 0

None of this addresses the question of how to find all the maximal elements (or positions of them).
That doesn't normally come up for me in practice, though I think it would be good to have.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文