Лучший алгоритм определения максимума и минимума в массиве чисел?

Я использую псевдокод здесь, но это в JavaScript. Имея самый эффективный алгоритм, я пытаюсь найти высокое и низкое количество массивов положительных целых чисел. Это то, что я придумал, но я не думаю, что это, вероятно, лучше всего, и было просто интересно, есть ли у кого-нибудь другие предложения.

var low = 1;
var high = 1;
for ( loop numbers ) {
    if ( number > high ) {
        high = number;
    }
    if ( low == 1 ) {
        low = high;
    }
    if ( number < low ) {
        low = number;
    }
}

Ответ 1

инициализировать высокий и низкий, чтобы быть первым элементом. имеет гораздо больше смысла, чем выбор произвольного "высокого" или "низкого" номера.

var myArray = [...],
    low = myArray[0],
    high = myArray[0]
;
// start looping at index 1
for (var i = 1, l = myArray.length; i < l; ++i) {
    if (myArray[i] > high) {
        high = myArray[i];
    } else if (myArray[i] < low) {
        low = myArray[i];
    }
}

или, избегая необходимости поиска массива несколько раз:

for (var i = 1, val; (val = myArray[i]) !== undefined; ++i) {
    if (val > high) {
        high = val;
    } else if (val < low) {
        low = val;
    }
}

Ответ 2

Вы должны сделать это в O(n), потому что вам нужно пройти через все (n) элементов, чтобы проверить их, потому что любой из элементов может быть min или max. (Если они уже не отсортированы.)

Другими словами, вам нужно пройти через все элементы и выполнить проверку max и min, как у вас.

Сортировка обычно в лучшем случае O(n*log(n)). Таким образом, он медленнее, чем одна прокрутка (O(n)).

Ответ 3

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

var low = numbers[0]; // first number in array
var high = numbers[0]; // first number in array
for ( loop numbers ) {
    if ( number > high ) {
        high = number;
    }
    if ( number < low ) {
        low = number;
    }
}

Ответ 4

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

Теперь, чтобы ответить на заданный вами вопрос.

Поскольку нет способа избежать просмотра каждого элемента списка, линейный поиск является наиболее эффективным алгоритмом. Требуется N раз, где N - количество элементов в списке. Выполнение всего этого в одном цикле более эффективно, чем вызов max(), а затем min() (который занимает 2 * N раз). Таким образом, ваш код в основном правильный, хотя он не учитывает отрицательные числа. Здесь он находится в Perl.

# Initialize max & min
my $max = $list[0];
my $min = $list[0];
for my $num (@list) {
     $max = $num if $num > $max;
     $min = $num if $num < $min;
}

Сортировка и захват первого и последнего элементов являются наименее эффективными. Требуется N * log (N), где N - количество элементов в списке.

Самый эффективный алгоритм min/max - это тот, где min/max пересчитывается каждый раз, когда элемент добавляется или удаляется из списка. Фактически, кэширование результата и исключение линейного поиска каждый раз. Время, затраченное на это, - это количество изменений списка. Это занимает, самое большее, M раз, где M - количество изменений независимо от того, сколько раз вы его называете.

Чтобы сделать это, вы можете рассмотреть дерево поиска, которое сохраняет свои элементы в порядке. Получение min/max в этой структуре - O (1) или O (log [n]) в зависимости от того, какой стиль дерева вы используете.

Ответ 5

Хотя он все еще является алгоритмом O (n), вы можете сделать это на 25% быстрее (т.е. константа пропорциональности равна 3/2 против 2), сначала сравнивая соседние элементы, затем сравнивая меньшее значение с min и больше до макс. Я не знаю javascript, но здесь он находится в С++:

std::pair<int, int> minmax(int* a, int n)
{
  int low = std::numeric_limits<int>::max();
  int high = std::numeric_limits<int>::min();

  for (int i = 0; i < n-1; i += 2) {
    if (a[i] < a[i+i]) {
      if (a[i] < low) {
        low = a[i];
      }
      if (a[i+1] > high) {
        high = a[i+1];
      }
    }
    else {
      if (a[i] > high) {
        high = a[i];
      }
      if (a[i+1] < low) {
        low = a[i+1];
      }
    }
  }

  // Handle last element if we've got an odd array size
  if (a[n-1] < low) {
    low = a[n-1];
  }
  if (a[n-1] > high) {
    high = a[n-1];
  }

  return std::make_pair(low, high);
} 

Ответ 6

var numbers = [1,2,5,9,16,4,6];

var maxNumber = Math.max.apply(null, numbers);
var minNumber = Math.min.apply(null, numbers);

Ответ 7

алгоритм nickf - лучший способ сделать это. В худшем случае алгоритм nickf выполняет 2 сравнения на число, в общей сложности 2n-2.

Мы можем сделать немного лучше. Когда вы сравниваете два элемента a и b, если a > b, мы знаем, что a не является min, а b не является максимальным. Таким образом мы используем всю доступную информацию для устранения как можно большего количества элементов. Для простоты предположим, что мы имеем четное число элементов.

Разделите их на пары: (a1, a2), (a3, a4) и т.д.

Сравните их, разбивая их на множество победителей и проигравших - это берет n/2 сравнения, давая нам два набора размера n/2. Теперь найдите максимальное количество победителей и минимум проигравших.

Сверху обнаружение min или max n элементов принимает n-1. Таким образом, время выполнения: n/2 (для начальных сравнений) + n/2 - 1 (максимум победителей) + n/2 - 1 (мин проигравших) = n/2 + n/2 + n/2 -2 = 3n/2 - 2. Если n нечетно, у нас есть еще один элемент в каждом из множеств, поэтому время выполнения будет 3n/2

На самом деле мы можем доказать, что это самый быстрый, что эта проблема может быть решена любым алгоритмом.

Пример:

Предположим, что наш массив равен 1, 5, 2, 3, 1, 8, 4 Разделите на пары: (1,5), (2,3) (1,8), (4, -). Сравните. Победители: (5, 3, 8, 4). Проигравшие - это (1, 2, 1, 4).

Сканирование победителей дает 8. Сканирование проигрывателей дает 1.

Ответ 8

Попробовав эти фрагменты для реального на V8, алгоритм Дрю Холла работает в 2/3 времени никфа, как и прогнозировалось. Сокращение числа циклов вместо сокращения сокращает его примерно до 59% времени (хотя это зависит от реализации). Только слегка проверены:

var A = [ /* 100,000 random integers */];

function minmax() {
    var low = A[A.length-1];
    var high = A[A.length-1];
    var i, x, y;
    for (i = A.length - 3; 0 <= i; i -= 2) {
        y = A[i+1];
        x = A[i];
        if (x < y) {
            if (x < low) {
                low = x;
            }
            if (high < y) {
                high = y;
            }
        } else {
            if (y < low) {
                low = y;
            }
            if (high < x) {
                high = x;
            }
        }
    }
    if (i === -1) {
        x = A[0];
        if (high < x) {
            high = x;
        } else if (x < low) {
            low = x;
        }
    }
    return [low, high];
}

for (var i = 0; i < 1000; ++i) { minmax(); }

Но человек, это довольно уродливо.

Ответ 9

Javascript-массивы имеют собственную функцию сортировки, которая принимает функцию, используемую для сравнения. Вы можете сортировать числа и просто взять голову и хвост, чтобы получить минимальный и максимальный значения.

var sorted = arrayOfNumbers.sort(function(a, b) { return a - b; }),
    ,min = sorted[0], max = sorted[sorted.length -1];

По умолчанию метод сортировки сортирует лексикографически (порядок словаря), поэтому вы должны передать функцию, чтобы использовать ее для получения числовой сортировки. Функция, которую вы передаете, должна вернуть 1, -1 или 0, чтобы определить порядок сортировки.

// standard sort function
function sorter(a, b) {
  if (/* some check */)
    return -1; // a should be left of b
  if (/*some other check*/)
    return 1; // a should be to the right of b
  return 0; // a is equal to b (no movement)
}

В случае чисел вы можете просто вычесть второе из первого параметра, чтобы определить порядок.

var numbers = [5,8,123,1,7,77,3.14,-5];

// default lexicographical sort
numbers.sort() // -5,1,123,3.14,5,7,77,8

// numerical sort
numbers.sort(function(a, b) { return a - b; }) // -5,1,123,3.14,5,7,77,8

Ответ 10

Единственная дальнейшая оптимизация, которую я предлагаю, - это оптимизация самого цикла. Быстрее подсчитывать, чем считать в JavaScript.

Ответ 11

Предполагая, что список еще не отсортирован, это самое лучшее, что вы можете сделать. Вы можете сэкономить себе сравнение, выполнив следующее (в псевдокоде):

low = +INFINITY
high = -INFINITY
for each n in numbers:
    if n < low:
        low = n
    if n > high:
        high = n

Это операция O (n), которая в основном лучше всего подходит.

Ответ 12

этот алгоритм работает для O (n) и не требует дополнительной памяти для хранения элементов...

enter code here
int l=0,h=1,index,i=3;
    if(a[l]>a[h])
                 swap(&a[l],&a[h]);
    for(i=2;i<9;i++)
    {
                                if(a[i]<a[l])
                                {
                                      swap(&a[i],&a[l]);  
                                }
                                if(a[i]>a[h])
                                {
                                             swap(&a[i],&a[h]);
                                }
    }
    printf("Low:  %d  High:  %d",a[0],a[1]);

Ответ 14

В python:

>>> seq = [1, 2, 3, 4, 5, 6, 7]
>>> max(seq)
7
>>> min(seq)
1