Проблема максимального подмассива - Maximum subarray problem

Визуализация изменения подмассивов в зависимости от начальной и конечной позиции образца. Каждый возможный непрерывный подмассив представлен точкой на цветной линии. Координата Y этой точки представляет собой сумму выборки. Его координата x представляет конец образца, а крайняя левая точка на этой цветной линии представляет начало образца. В этом случае массив, из которого берутся образцы, равен [2, 3, -1, -20, 5, 10].

В Информатика, то проблема подмассива максимальной суммы - задача найти непрерывный подмассив с наибольшей суммой в заданном одномерном массив [1 ... n] чисел. Формально задача найти индексы и с , такая что сумма

как можно больше. (Некоторые постановки задачи также позволяют рассматривать пустой подмассив; по соглашению сумма всех значений пустого подмассива равен нулю.) Каждое число во входном массиве A может быть положительным, отрицательным или нулевым.[1]

Например, для массива значений [−2, 1, −3, 4, −1, 2, 1, −5, 4] непрерывный подмассив с наибольшей суммой равен [4, −1, 2, 1] , с суммой 6.

Некоторые свойства этой проблемы:

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

Эта проблема может быть решена с помощью нескольких различных алгоритмических методов, включая грубую силу,[2] разделяй и властвуй,[3] динамическое программирование,[4] и сокращение до кратчайших путей.[нужна цитата ]

История

Задача о максимуме подмассива была предложена Ульф Гренандер в 1977 г. как упрощенная модель для максимальная вероятность оценка закономерностей в оцифрованных изображениях.[5]

Гренандер искал прямоугольный подмассив с максимальной суммой в двумерном массиве действительных чисел. Алгоритм грубой силы для двумерной задачи выполняется в О(п6) время; поскольку это происходило слишком медленно, Гренандер предложил одномерную задачу, чтобы понять ее структуру. Гренандер вывел алгоритм, решающий одномерную задачу в О(п2) время,[примечание 1]улучшение времени работы грубой силы О(п3). Когда Майкл Шамос услышав о проблеме, он в одночасье придумал О(п бревно п) алгоритм разделяй и властвуй Вскоре после этого Шамос описал одномерную проблему и ее историю на Университет Карнеги Меллон семинар посетили Джей Кадейн, который за минуту разработал О(п) -временной алгоритм,[5][6][7] что максимально быстро.[заметка 2] В 1982 г. Дэвид Грис получил такой же О(п) -времени, применяя Dijkstra «стандартная стратегия»;[8] в 1989 г., Ричард Берд получил его чисто алгебраическим манипулированием алгоритма грубой силы, используя Формализм Берда – Меертенса.[9]

Двумерное обобщение Гренандера может быть решено за O (п3) времени либо с помощью алгоритма Кадане в качестве подпрограммы, либо с помощью подхода «разделяй и властвуй». Чуть более быстрые алгоритмы на основе умножение матрицы расстояний были предложены Тамаки и Токуяма (1998) и по Такаока (2002). Есть некоторые свидетельства того, что не существует значительно более быстрого алгоритма; алгоритм, который решает двумерную задачу о максимуме подмассива в O (п3 − ε) времени для любого ε> 0 означало бы аналогичный быстрый алгоритм для кратчайшие пути для всех пар проблема.[10]

Приложения

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

Анализ геномной последовательности использует алгоритмы максимального подмассива для идентификации важных биологических сегментов белковых последовательностей.[нужна цитата ] Эти проблемы включают консервативные сегменты, GC-богатые области, тандемные повторы, фильтр низкой сложности, ДНК-связывающие домены и области с высоким зарядом.[нужна цитата ]

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

Алгоритм Кадане

Кадане алгоритм сканирует данный массив слева направо. в -й шаг, он вычисляет подмассив с наибольшей суммой, заканчивающейся на ; эта сумма сохраняется в переменной current_sum.[заметка 3]Более того, он вычисляет подмассив с наибольшей суммой где-либо в , поддерживается в переменной best_sum,[примечание 4]и легко получается как максимум из всех значений current_sum видно до сих пор, ср. строка 7 алгоритма.

Как инвариант цикла, в -й шаг, старое значение current_sum держит максимум над всеми суммы .[примечание 5]Следовательно, current_sum[примечание 6]это максимум по всем суммы . Чтобы расширить последний максимум, чтобы охватить также случай , достаточно рассмотреть также пустой подмассив . Это делается в строке 6 путем присвоения current_sum как новое значение current_sum, который после этого имеет максимум по всем суммы .

Таким образом, проблема может быть решена с помощью следующего кода,[4][7] выражено здесь в Python:

1 def max_subarray(числа):2     "" "Найдите наибольшую сумму любого непрерывного подмассива." ""3     best_sum = 0  # или: float ('- inf')4     current_sum = 05     за Икс в числа:6         current_sum = Максимум(0, current_sum + Икс)7         best_sum = Максимум(best_sum, current_sum)8     вернуть best_sum

Эта версия алгоритма вернет 0, если входные данные не содержат положительных элементов (в том числе, когда вход пуст). Для варианта задачи, запрещающего пустые подмассивы, best_sum вместо этого следует инициализировать отрицательную бесконечность[11] а также в цикле for current_sum следует обновить как макс (х, текущая_сумма + х).[примечание 7]В этом случае, если входные данные не содержат положительного элемента, возвращаемое значение - это значение самого большого элемента (т. Е. Наименьшее отрицательное значение) или отрицательная бесконечность, если вход был пуст.

Алгоритм можно изменить, чтобы отслеживать начальные и конечные индексы максимального подмассива:

 1 def max_subarray(числа): 2     "" "Найдите непрерывный подмассив с наибольшей суммой." "" 3     best_sum = 0  # или: float ('- inf') 4     best_start = best_end = 0  # или: Нет 5     current_sum = 0 6     за current_end, Икс в перечислять(числа): 7         если current_sum <= 0: 8             # Начать новую последовательность с текущего элемента 9             current_start = current_end10             current_sum = Икс11         еще:12             # Расширить существующую последовательность текущим элементом13             current_sum += Икс14 15         если current_sum > best_sum:16             best_sum = current_sum17             best_start = current_start18             best_end = current_end + 1  # +1 делает 'best_end' эксклюзивным19 20     вернуть best_sum, best_start, best_end

В Python массивы индексируются, начиная с 0, а конечный индекс обычно исключается, так что подмассив [22, 33] в массиве [-11, 22, 33, -44] будет начинаться с индекса 1 и заканчиваться индексом 3.

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

Сложность выполнения алгоритма Кадане составляет .[4][7]

Обобщения

Подобные проблемы могут возникать и для многомерных массивов, но их решения более сложны; см., например, Такаока (2002). Бродал и Йоргенсен (2007) показал, как найти k наибольшие суммы подмассивов в одномерном массиве за оптимальное время .

Максимальная сумма k-связанные подмассивы также могут быть вычислены в оптимальной временной оценке .[12]

Смотрите также

Примечания

  1. ^ Используя предварительно вычисленную таблицу накопленных сумм для вычисления суммы подмассива в постоянное время
  2. ^ поскольку каждый алгоритм должен хотя бы один раз просканировать массив, который уже принимает О(п) время
  3. ^ названный MaxEndingHere в Бентли (1989), и c в Грис (1982)
  4. ^ названный МаксСофар в Бентли (1989), и s в Грис (1982)
  5. ^ Эта сумма когда , соответствующий пустому подмассиву .
  6. ^ В коде Python выражается как Икс, с индексом слева неявно.
  7. ^ Хотя последняя модификация не упоминается Бентли (1989), он поддерживает инвариант измененного цикла current_sum в начале -й шаг.

Рекомендации

внешняя ссылка