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$
2
  • $\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$ Oct 3, 2018 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, 2018 at 14:13

3 Answers 3

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$
3
  • $\begingroup$ That's elegant. $\endgroup$
    – Christoph
    Oct 4, 2018 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, 2018 at 14:47
  • $\begingroup$ @Christoph, both great points. $\endgroup$
    – kglr
    Oct 5, 2018 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$
2
  • $\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, 2018 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$ Oct 3, 2018 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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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