Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a faster way to find both Min and Max values?

Often I have written: {Min@#, Max@#} &

Yet this seems inefficient, as the expression must be scanned twice, once to find the minimum value, and once to find the maximum value. Is there a faster way to do this? The expression is often a tensor or array.

like image 233
Mr.Wizard Avatar asked May 04 '11 12:05

Mr.Wizard


People also ask

What is the best algorithm to find the maximum and the minimum numbers in an array?

We traverse the array from i = 1 to n - 1 and compare each element with min and max. If (X[i] < min): We have found a value smaller than minimum till ith index. So update min with X[i] i.e min = X[i]. else If(X[i] > max): We have found a value greater than maximum till ith index.

How do you find the minimum and maximum values of a function?

We will set the first derivative of the function to zero and solve for x to get the critical point. If we take the second derivative or f''(x), then we can find out whether this point will be a maximum or minimum. If the second derivative is positive, it will be a minimum value.

What is the time complexity of MIN () and MAX () method?

Return max and min. Time Complexity is O(n) and Space Complexity is O(1). For each pair, there are a total of three comparisons, first among the elements of the pair and the other two with min and max.

What is the best time complexity of an algorithm needed to find the largest number in an ordered array of integers Given that the length is known?

We are given an integer array of size N or we can say number of elements is equal to N. We have to find the largest/ maximum element in an array. The time complexity to solve this is linear O(N) and space compexity is O(1).


2 Answers

This beats it by a bit.

minMax = Compile[{{list, _Integer, 1}},
   Module[{currentMin, currentMax},
    currentMin = currentMax = First[list];
    Do[
     Which[
      x < currentMin, currentMin = x,
      x > currentMax, currentMax = x],
     {x, list}];
    {currentMin, currentMax}],
   CompilationTarget -> "C",
   RuntimeOptions -> "Speed"];
v = RandomInteger[{0, 1000000000}, {10000000}];
minMax[v] // Timing

I think it's a little faster than Leonid's version because Do is a bit faster than For, even in compiled code.

Ultimately, though, this is an example of the kind of performance hit you take when using a high level, functional programming language.

Addition in response to Leonid:

I don't think that the algorithm can account for all the time difference. Much more often than not, I think, both tests will be applied anyway. The difference between Do and For is measurable, however.

cf1 = Compile[{{list, _Real, 1}},
   Module[{sum},
    sum = 0.0;
    Do[sum = sum + list[[i]]^2,
     {i, Length[list]}];
    sum]];
cf2 = Compile[{{list, _Real, 1}},
   Module[{sum, i},
    sum = 0.0;
    For[i = 1, i <= Length[list],
     i = i + 1, sum = sum + list[[i]]^2];
    sum]];
v = RandomReal[{0, 1}, {10000000}];
First /@{Timing[cf1[v]], Timing[cf2[v]]}

{0.685562, 0.898232}
like image 128
Mark McClure Avatar answered Oct 15 '22 13:10

Mark McClure


I think this is as fast as it gets, within Mathematica programming practices. The only way I see to attempt to make it faster within mma is to use Compile with the C compilation target, as follows:

getMinMax = 
 Compile[{{lst, _Real, 1}},
   Module[{i = 1, min = 0., max = 0.},
       For[i = 1, i <= Length[lst], i++,
        If[min > lst[[i]], min = lst[[i]]];
        If[max < lst[[i]], max = lst[[i]]];];
        {min, max}], CompilationTarget -> "C", RuntimeOptions -> "Speed"]

However, even this seems to be somewhat slower than your code:

In[61]:= tst = RandomReal[{-10^7,10^7},10^6];

In[62]:= Do[getMinMax[tst],{100}]//Timing
Out[62]= {0.547,Null}

In[63]:= Do[{Min@#,Max@#}&[tst],{100}]//Timing
Out[63]= {0.484,Null}

You probably can write the function entirely in C, and then compile and load as dll - you may eliminate some overhead this way, but I doubt that you will win more than a few percents - not worth the effort IMO.

EDIT

It is interesting that you may significantly increase the speed of the compiled solution with manual common subexpression elimination (lst[[i]] here):

getMinMax = 
 Compile[{{lst, _Real, 1}},
   Module[{i = 1, min = 0., max = 0., temp},
     While[i <= Length[lst],
       temp = lst[[i++]];
       If[min > temp, min = temp];
       If[max < temp, max = temp];];
       {min, max}], CompilationTarget -> "C", RuntimeOptions -> "Speed"]

is a little faster than {Min@#,Max@#}.

like image 38
Leonid Shifrin Avatar answered Oct 15 '22 12:10

Leonid Shifrin