2
$\begingroup$

I need to create code that identifies the positions of all maximum elements of a list. I already know that if myList contains only numbers, I can do

mList={0,1}    
Position[myList,Max[myList]] 

This will return, as expected, {{2}}.

However my case is a bit more complicated because the list contains variables.

Here is an example to illustrate:

myList = {1, a};
Position[myList, Max[myList]]

This returns {}.

I fully understand that this is because none of the elements in myList matches the expression Max[1,a].

I've tried various variants of the following, to no avail:

PiecewiseExpand[Max[myList]]
Position[myList,%]

The first line returns, as expected: "1 if a<=1, a otherwise". But the second line returns {}. (Again, I understand that this is because the list elements don't match the piecewise function.)

Is there a way to change this code so that I get the following desired result:

"1 if a <=1; 2 otherwise"

(That is: I want the output to be the unevaluated conditional statement.)

$\endgroup$
  • $\begingroup$ What is the expected output? an unevaluated form that only evaluates when all symbolic variables are assigned a numerical value? That is easy. Or a piecewise list of positions for each case? That is doable, but requires more work. $\endgroup$ – AccidentalFourierTransform Oct 3 '18 at 13:52
  • $\begingroup$ The output should be a list of positions for each case. I've edited the question. Thanks. $\endgroup$ – Christoph Oct 3 '18 at 14:13
0
$\begingroup$
posMax[list_] := Module[{pw = PiecewiseExpand[Max[list]]}, 
  If[NumericQ@pw, Position[list, pw], MapAt[Position[list, #] &, pw, {{1, All, 1}, {2}}]]]

Examples:

posMax[{0, 3, 2}]

{{2}}

posMax[{0, a}] // TeXForm

$\begin{cases} \left( \begin{array}{c} 2 \\ \end{array} \right) & a>0 \\ \left( \begin{array}{c} 1 \\ \end{array} \right) & \text{True} \end{cases}$

posMax[{0, 1, a, b}] // TeXForm

$\begin{cases} \left( \begin{array}{c} 2 \\ \end{array} \right) & a\leq 1\land b\leq 1 \\ \left( \begin{array}{c} 3 \\ \end{array} \right) & a>1\land a-b\geq 0 \\ \left( \begin{array}{c} 4 \\ \end{array} \right) & \text{True} \end{cases}$

$\endgroup$
  • $\begingroup$ That's elegant. $\endgroup$ – Christoph Oct 4 '18 at 13:36
  • $\begingroup$ Two small comments: (1) I think in "Position[list,pw]" and "Position[list,#]" we need to add ",1]" at the end so that we don't get patterns matching at deeper levels. Also, the "If[NumericQ@pw,..." condition might be replaced by "If[Head[pw]=!=Piecewise" $\endgroup$ – Christoph Oct 5 '18 at 14:47
  • $\begingroup$ @Christoph, both great points. $\endgroup$ – kglr Oct 5 '18 at 15:32
1
$\begingroup$

What about this?

f[list_?(VectorQ[#, NumericQ] &)] := Ordering[list, -1][[1]]

f[{0, a}]
f[{0, a}] /. a -> 1
f[{0, a}] /. a -> -1

f[{0, a}]

2

1

Notice that I used Ordering instead of Position and Max.

$\endgroup$
  • $\begingroup$ Hi Henrik, thanks for the quick response! Unfortunately, that doesn't solve my problem. To clarify: I literally want the output to be "1 if a <=1 and 2 otherwise". That is, I want the output to be another piecewise function. Apologies if I have misunderstood your solution. $\endgroup$ – Christoph Oct 3 '18 at 14:02
  • $\begingroup$ For two-element list, you can write it down by hand with Piecewise. For longer list, the resulting expression is going to be huge and won't result in efficient code. $\endgroup$ – Henrik Schumacher Oct 3 '18 at 14:57
0
$\begingroup$

As a follow-up: The following solution is not an improvement upon the one offered by AccidentalFourierTransform. I'm just posting it because it may help to understand how his solution works. For anyone exploring this, it helps to use FullForm to see what a piecewise function actually looks like to Mathematica. (Turns out that if f is piecewise, then f[[1]] is a nested list of lists of the form {value, condition}, and f[[2]] is the value if all conditions are false.)

maxElements[list_] /; And @@ NumericQ /@ list := 
 Position[list, Max[list]]
maxElements[list_] := Module[
   {snap},
   snap = PiecewiseExpand[Max[list]];
   ReplacePart[
    snap, {1 -> 
      Map[MapAt[Flatten[Position[list, #]] &, 1], snap[[1]]], 
     2 -> Flatten[Position[list, snap[[2]]]]}]
   ];
maxElement[{0, 3, 2}]
maxElement[{0, a}]
maxElement[{0, 1, a, b}]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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