Suppose we have a list of values and a function f. I want to find which of the elements maximizes the return value of the function in Mathematica. Let call the function ListMaxArg. On the following example,

ListMaxArg[Total, {{1, 2, 3}, {10}}]

it should return {10}. Is there any such function in Mathematica's library? If not, what is the simplest way to write it? I can for sure write the function using a loop, but I am looking for something more functional style.

link|improve this question

71% accept rate
I'm going to migrate this question to the mathematica.SE site, since I think it's a better fit there. There will be a link that appears below the question here that you can follow to the new location of your question. – Zev Chonoles Feb 23 at 1:52
1  
1  
@Mr.Wizard Yes, this question is a generalization of '1342'. – Brett Champion Feb 23 at 5:01
feedback

migrated from math.stackexchange.com Feb 23 at 1:57

This question came from our site for people studying math at any level and professionals in related fields.

2 Answers

up vote 12 down vote accepted

While using Ordering as in Mr.Wizard's answer will most likely be the fastest non-compiled solution, it will not return all possible arguments that maximize the return value. Here's a simple way of writing a function that does this:

listMaxArg[f_, list_] := With[{max = f /@ list // Max}, Select[list, f@# == max &]]

Here's an example that compares the two solutions:

a = {{1, 2, 3}, {10}, {6, 4}};
listMaxArg[Total, a] 
(* Out[1]= {{10}, {6, 4}} *)

listMaxArgMrWiz[Total, a]
(*Out[2]= {{6, 4}} *)
link|improve this answer
+1. Note that Pick will be faster than Select here, about twice faster for really long lists. – Leonid Shifrin Feb 23 at 11:43
@LeonidShifrin Thanks. I did remember that Pick should be faster than Select, and tried listMaxArgPick[f_, list_] := Pick[list, Unitize[##/Max[##] &@(f /@ list), 1], 1]. Unfortunately, this unpacks, which slows it down, so I put it aside to poke at later. Can you point me to what I'm doing incorrectly (or how that can be written better)? I generated a random list with l = Table[RandomInteger[5, RandomInteger[{1, 5}]], {100000}]; – R.M Feb 23 at 12:48
3  
Pick has an optional third parameter, which allows to specify which element you want to be picked. The code I have is thus: listMaxArgAlt[f_, lst_] := Pick[lst, #, Max[#]] &[f /@ lst], and it is about twice faster, since I don't have to play games with Unitize. – Leonid Shifrin Feb 23 at 12:57
It should be noted that Pick unpacks in version 7. – Mr.Wizard Feb 23 at 13:29
1  
Interestingly, Select does not unpack, because for each element, Total applied to it, does not unpack. Still, it is slower than Pick here, because of the overhead of extra invocation of Function[f@#==max] (symbolic overhead). Pick has to unpack because it performs structural comparison (based on internal equivalent of SameQ actually, not Equal). This situation is similar to that in this question. This is an interesting and didactic example IMO, this present one. – Leonid Shifrin Feb 23 at 14:30
show 8 more comments
feedback

If I understand the question this should be fastest:

listMaxArg[f_, L_List] := L ~Extract~ Ordering[f /@ L, -1]

listMaxArg[Total, {{1, 2, 3}, {10}}]
{10}

You could also find the top n values by using -n as the second argument to Ordering.
This could be included in the function, e.g. listMaxArg[f_, L_List, n_Integer] := . . .


This method should be fast for finding multiple maximum values:

listMaxArg[f_, L_List] := L ~Extract~ Position[#, Max@#] &[f /@ L]

listMaxArg[Total, {{1, 2, 3}, {10}, {6, 2}, {3, 7}}]
{{10}, {3, 7}}

I argue the superiority of the Extract-Position method (from Arnoud Buzing) over Select on the basis of timings. I will use Tr in the place of Total as it is faster on Packed Arrays, and therefore better shows the overhead of each method.

listMaxArgRM[f_, list_] :=
  With[{max = f /@ list // Max}, Select[list, f@# == max &]]

listMaxArgMrW[f_, L_List] :=
  L ~Extract~ Position[#, Max@#] &[f /@ L]

SeedRandom[1]
list = RandomInteger[7, #] & /@ RandomInteger[{1, 5}, 1000000];

r1 = listMaxArgRM[Tr, list]; // AbsoluteTiming
r2 = listMaxArgMrW[Tr, list]; // AbsoluteTiming
r1 === r2
{0.6770387, Null}

{0.2080119, Null}

True

As you can see listMaxArgMrW is over three times as efficient as listMaxArgRM, at least in version 7.

link|improve this answer
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.