Каков наилучший способ получить минимальное или максимальное значение из массива чисел?

Допустим у меня есть массив чисел: [2,3,3,4,2,2,5,6,7,2]

каков наилучший способ найти минимальное или максимальное значение в этом массиве?

прямо сейчас, чтобы получить максимум, я перебираю массив и сбрасываю переменную на значение, если оно больше существующего значения:

var myArray:Array /* of Number */ = [2,3,3,4,2,2,5,6,7,2];

var maxValue:Number = 0;

for each (var num:Number in myArray)
{
    if (num > maxValue)
        maxValue = num;
}

Это просто не кажется лучшим способом сделать это (я стараюсь избегать циклов, когда это возможно).

17 ответов


теоретические ответы от всех остальных, все аккуратно, но давайте быть прагматичными. ActionScript предоставляет необходимые инструменты, так что вам даже не придется писать цикл в этом случае!

во-первых, обратите внимание, что Math.min() и Math.max() может принимать любое количество аргументов. Кроме того, важно понимать apply() методом Function объекты. Он позволяет передавать аргументы функции с помощью Array. Давайте воспользуемся обоими:

var myArray:Array = [2,3,3,4,2,2,5,6,7,2];
var maxValue:Number = Math.max.apply(null, myArray);
var minValue:Number = Math.min.apply(null, myArray);

здесь лучшая часть: "цикл" фактически запускается с использованием собственного кода (внутри Flash Player), поэтому он быстрее, чем поиск минимального или максимального значения с помощью чистого цикла ActionScript.


нет никакого надежного способа получить минимум / максимум без тестирования каждого значения. Вы не хотите пробовать сортировку или что-то в этом роде, прогулка по массиву-Это O(n), что лучше, чем любой алгоритм сортировки может сделать в общем случае.


Если

  1. массив не отсортирован
  2. Поиск min и max выполняется одновременно

тогда есть алгоритм, который находит min и max в 3n / 2 количестве сравнений. Что нужно сделать, так это обработать элементы массива попарно. Большую часть пары следует сравнить с текущей max, а меньшую часть пары - с текущей min. Кроме того, нужно проявлять особую осторожность, если массив содержит нечетные число элементов.

в коде C++ (заимствование некоторого кода из Mehrdad).

struct MinMax{
   int Min,Max;
}

MinMax FindMinMax(int[] array, int start, int end) {
   MinMax  min_max;
   int index;
   int n = end - start + 1;//n: the number of elements to be sorted, assuming n>0
   if ( n%2 != 0 ){// if n is odd

     min_max.Min = array[start];
     min_max.Max = array[start];

     index = start + 1;
   }
   else{// n is even
     if ( array[start] < array[start+1] ){
       min_max.Min = array[start];
       min_max.Max = array[start+1];
     }
     else{
       min_max.Min = array[start+1];
       min_max.Max = array[start];
     }
     index = start + 2;
   }

   int big, small;
   for ( int i = index; i < n-1; i = i+2 ){
      if ( array[i] < array[i+1] ){ //one comparison
        small = array[i];
        big = array[i+1];
      }
      else{
        small = array[i+1];
        big = array[i];
      }
      if ( min_max.Min > small ){ //one comparison
        min_max.Min = small;
      }
      if ( min_max.Max < big ){ //one comparison
        min_max.Max = big;
      }
   }

   return min_max;
}

очень легко увидеть, что количество сравнений, которые он принимает, составляет 3n / 2. Цикл выполняется n / 2 раза и на каждой итерации выполняется 3 сравнения. Это, вероятно, оптимальный вариант. В данный момент я не могу указать на определенный источник этого. (Но, думаю, я где-то видел доказательство этого.)

рекурсивное решение, данное Mehrdad выше, вероятно, также достигает этого минимального количества сравнений (последняя строка должна быть изменена). Но с тем же количеством сравнений итеративное решение всегда будет бить рекурсивное решение из-за накладных расходов в вызове функции, как он упоминал. Однако, если вы заботитесь только о поиске min и max нескольких чисел (как это делает Эрик Белэйр), никто не заметит никакой разницы в сегодняшнем компьютере с любым из подходов выше. Для большого массива, разница может быть существенной.

хотя это решение и решение, данное Мэтью Брубейкером, имеет сложность O(n), на практике следует тщательно оценивать скрытые константы. Число сравнений в его решении равно 2n. Ускорение, полученное с помощью решения с сравнениями 3n/2, в отличие от сравнений 2n, было бы заметно.


Если массив отсортирован, это лучшее, что вы собираетесь получить. Если он отсортирован, просто возьмите первый и последний элементы.

конечно, если он не отсортирован, то сортировка сначала и захват первого и последнего гарантированно будет менее эффективным, чем просто цикл через один раз. Даже лучшие алгоритмы сортировки должны смотреть на каждый элемент более одного раза(в среднем O (log N) раз для каждого элемента. Это o (n * Log N) всего. Простое сканирование только один раз O (N).

Если вы хотите быстрый доступ к самому большому элементу в структуре данных, посмотрите на кучи для эффективного способа сохранить объекты в некотором порядке.


вы должны пройти через массив, нет другого способа проверить все элементы. Только одно исправление для кода-если все элементы отрицательны, maxValue будет 0 в конце. Вы должны инициализировать его с наименьшее возможное значение типа integer.
И если вы собираетесь искать массив много раз, рекомендуется сначала отсортировать его, чем поиск быстрее (двоичный поиск), а минимальные и максимальные элементы-только первые и последние.


зависит от того, что вы называете "лучшей."С теоретической точки зрения, вы не можете решить проблему менее чем за O(n) в детерминированной машине Тьюринга.

наивный алгоритм слишком петля и обновление min, max. Однако рекурсивное решение потребует меньше сравнений, чем наивный алгоритм, если вы хотите получить min, max одновременно (это не обязательно быстрее из-за накладных расходов на вызов функции).

struct MinMax{
   public int Min,Max;
}

MinMax FindMinMax(int[] array, int start, int end) {
   if (start == end)
      return new MinMax { Min = array[start], Max = array[start] };

   if (start == end - 1)
      return new MinMax { Min = Math.Min(array[start], array[end]), Max = Math.Max(array[start], array[end]) } ;

   MinMax res1 = FindMinMax(array, start, (start + end)/2);
   MinMax res2 = FindMinMax(array, (start+end)/2+1, end);
   return new MinMax { Min = Math.Min(res1.Min, res2.Min), Max = Math.Max(res1.Max, res2.Max) } ;
}

самым простым решением было бы отсортировать и получить первый и последний пункт, хотя это, очевидно, не самый быстрый;)

лучшее решение, с точки зрения производительности, чтобы найти минимум или максимум-это наивный алгоритм, который вы написали (с одной петлей).


математика.max () фактически является кодом as3, скомпилированным в Коды операций AVM2, и как таковой не является более "родным", чем любой другой код as3. Как следствие, это не обязательно самая быстрая реализация.

на самом деле, учитывая, что он работает на типе массива, он медленнее, чем тщательно написанный код usign Vector:

Я сделал быстрое эталонное сравнение нескольких наивных векторных и массивных реализаций математики.max, используя PerformanceTest gskinner (вектор и массив заполняются одинаковые случайные числа). Самая быстрая векторная реализация оказалась более чем в 3 раза быстрее, чем математика.Макс с последним AIR SDK/release player (flash player WIN 14,0,0,122 RELEASE, скомпилированный с AIR SDK 14):

среднее 3,5 мс для 1,000,000 значений, по сравнению с математикой.max () среднее значение 11ms:

function max(values:Vector.<Number>):Number
{
    var max:Number = Number.MIN_VALUE;
    var length:uint = values.length;
    for (var i:uint = 0; i < length ; ++i)
        if (values[i] > max)
            max = values[i];
    return max;
}

вывод заключается в том, что если вас беспокоит производительность, вы должны использовать вектор над массивом в любом месте, где вы можете в первую очередь, и не всегда полагаться на значение по умолчанию реализации, особенно когда они заставляют использовать Array

PS: та же реализация с циклом for each () на 12 раз медленнее ...!


Это зависит от реальных требований приложений.

Если ваш вопрос чисто гипотетический, то основы уже были объяснены. Это типичная проблема поиска и сортировки. Уже упоминалось, что алгоритмически вы не достигнете лучшего, чем O(n) для этого случая.

однако, если вы смотрите на практическое использование, все становится более интересным. Затем вам нужно будет рассмотреть, насколько велик массив, и процессы, участвующие в добавление и удаление из набора данных. В этих случаях лучше всего взять вычислительный "удар" во время вставки / удаления, сортируя на лету. Вставки в предварительно отсортированный массив не так дорого.

самый быстрый ответ на запрос Min Max всегда будет из отсортированного массива, потому что, как упоминали другие, вы просто берете первый или последний элемент, давая вам стоимость O(1).

для немного больше технического объяснения на вычислительном затраты, связанные и Big O notation, проверьте статью Википедии здесь.

Ник.


Если вы строите массив после и хотите найти максимум только один раз, итерация-лучшее, что вы можете сделать.

когда вы хотите изменить массив и иногда хочется узнать максимальный элемент, вы должны использовать Приоритет В Очереди. Одной из лучших структур данных для этого является Кучи Фибоначчи, если это слишком сложно использовать Двоичные Кучи что медленнее, но все же хорошо.

найти минимум и максимум, просто постройте две кучи и измените знак чисел в одной из них.


прошу учесть, что сортировка массива будет быстрее, чем цикл до определенного размера массива. Если Ваш массив мал (и это будет так в любое время), то ваше решение прекрасно. Но если он может стать слишком большим, вы должны использовать условие для использования подхода сортировки, когда массив мал, и обычной итерации, когда он слишком велик


Если вы хотите найти как min, так и max одновременно, цикл можно изменить следующим образом:

int min = int.maxValue;
int max = int.minValue;

foreach num in someArray {
  if(num < min)
    min = num;
  if(num > max)
    max = num;
}

Это должно получить время достижения O (n).


кратчайший путь :

математика.минута.apply (null,array); //это вернет минимальное значение из array
Математика.максимум.apply (null,array); //это вернет максимальное значение из array

другойспособ получить мин и Макс значение из массива

 function maxVal(givenArray):Number
    {
    var max = givenArray[0];
    for (var ma:int = 0; ma<givenArray.length; ma++)
    {
    if (givenArray[ma] > max)
    {
    max = givenArray[ma];
    }
    }
    return max;
    }

    function minVal(givenArray):Number
    {
    var min = givenArray[0];
    for (var mi:int = 0; mi<givenArray.length; mi++)
    {
    if (givenArray[mi] < min)
    {
    min = givenArray[mi];
    }
    }
    return min;
    }

Как вы можете видеть, код в обеих этих функциях очень похож. Функция устанавливает переменную-max (или min), а затем проходит через массив с циклом, проверяя каждый следующий элемент. Если следующий элемент выше чем ток, установите его в max (или min). В конце концов, верните номер.


существует несколько способов сделать это.

  1. грубой силы. Линейный поиск для min и Max отдельно. (2N сравнение и 2n шагов)
  2. повторите линейно и проверьте каждое число как для min, так и для max. (2Н сравнения)
  3. использовать Разделяй и властвуй. (между сравнениями 2N и 3N/2)
  4. сравнить по парам объясняется ниже (3N / 2 Сравнения)

как найти max. и мин. в массиве с использованием минимальных сравнений?


Если вы действительно параноик о скорости, времени выполнения и количестве сравнений, также обратитесь к http://www.geeksforgeeks.org/maximum-and-minimum-in-an-array/


алгоритм MaxMin(первый, последний, макс, мин)

//этот алгоритм хранит самый высокий и самый низкий элемент

//значения глобального массива A в глобальных переменных max и min

/ / tmax и tmin являются временными глобальными переменными

{
if (first==last) //Sub-array contains single element
 {
    max=A[first];
    min=A[first];
 }
 else if(first+1==last) //Sub-array contains two elements
  {
     if(A[first]<A[Last])
      {
      max=a[last];  //Second element is largest
      min=a[first]; //First element is smallest
      }
   else
   {
     max=a[first]; //First element is Largest 
     min=a[last];  //Second element is smallest
   }
}
else
 //sub-array contains more than two elements
{
 //Hence partition the sub-array into smaller sub-array 
 mid=(first+last)/2;
 //Recursively solve the sub-array
 MaxMin(first,mid,max,min);
 MaxMin(mid+1,last,tmax,tmin);
 if(max<tmax)
  {
     max=tmax;
  }
    if(min>tmin)
  {
   min=tmin;
  }
 }
}

Ниже приведено решение с o (n): -

public static void findMaxAndMinValue(int A[]){
    int min =0, max = 0;
    if(A[0] > A[1] ){
        min = A[1];
        max = A[0];
    }else{
        max = A[1];
        min = A[0];
    }
    for(int i = 2;i<A.length ;i++){
        if(A[i] > max){
            max = A[i];
        }
        if(min > A[i]){
            min = A[i];
        }
    }
    System.out.println("Maxinum Value is  "+min+" & Minimum Value is  "+max);
}

поразило, что здесь никто не упомянул параллелизм.

Если у вас действительно огромный массив, вы можете использовать parallel-for, на поддиапазонах. В конце сравните все поддиапазоны. Но параллелизм также имеет ширину некоторого штрафа, поэтому это не будет оптимизировать на небольших массивах. Однако, если у вас есть огромные наборы данных, это начинает иметь смысл, и вы получаете сокращение времени, близкое к количеству потоков, выполняющих тест.


прочитав комментарии всех (Спасибо за интерес), я обнаружил, что "лучший" способ (наименьшее количество кода, наилучшая производительность) сделать это - просто отсортировать массив, а затем захватить первое значение в массиве:

var myArray:Array /* of Number */ = [2,3,3,4,2,2,5,6,7,2];

myArray.sort(Array.NUMERIC);

var minValue:int = myArray[0];

Это также работает для массива объектов - вы просто использовать массив.функция sortOn () и укажите свойство:

// Sample data
var myArray:Array /* of XML */ = 
    [
    <item level="2" name="a" />
    <item level="3" name="b" />
    <item level="3" name="c" />
    <item level="2" name="d" />
    <item level="5" name="e" />
    ]

// Perform a descending sort on the specified attribute in Array to get the maximum value
myArray.sortOn("@level", Array.DESCENDING | Array.NUMERIC);

var lowestLevel:int = myArray[0].@level;

Я надеюсь, что это поможет кому-то еще когда-нибудь!