У меня есть жадный алгоритм, который, я подозреваю, может быть правильным, но я не уверен. Как мне проверить, правильно ли это? Какие методы использовать для доказательства правильности жадного алгоритма? Существуют ли общие модели или методы?
Я надеюсь, что это станет справочным вопросом, на который можно указать новичкам; следовательно, его более широкий, чем обычно, объем. Пожалуйста, позаботьтесь о том, чтобы дать общие, дидактически представленные ответы, которые иллюстрируются хотя бы одним примером, но тем не менее охватывают многие ситуации. Благодарность!
Ответы:
В конечном итоге вам понадобится математическое доказательство правильности. Ниже я приведу некоторые методы доказательства, но сначала, прежде чем углубляться в это, позвольте мне сэкономить немного времени: прежде чем искать доказательство, попробуйте случайное тестирование.
Случайное тестирование
В качестве первого шага я рекомендую использовать случайное тестирование для проверки вашего алгоритма. Удивительно, насколько это эффективно: по моему опыту, для жадных алгоритмов случайное тестирование кажется неоправданно эффективным. Потратьте 5 минут на написание своего алгоритма, и вы можете сэкономить час или два, пытаясь придумать доказательство.
Основная идея проста: реализовать свой алгоритм. Кроме того, реализуйте эталонный алгоритм, который, как вы знаете, является правильным (например, тот, который исчерпывающе пробует все возможности и выбирает лучшее). Хорошо, если ваш эталонный алгоритм асимптотически неэффективен, так как вы будете запускать его только на небольших проблемных экземплярах. Затем случайным образом сгенерируйте миллион маленьких проблемных экземпляров, запустите оба алгоритма для каждого и проверьте, дает ли алгоритм-кандидат правильный ответ в каждом случае.
Опытным путем, если ваш жадный алгоритм кандидата неверен, обычно вы часто обнаруживаете это во время случайного тестирования. Если это кажется правильным во всех тестовых случаях, то вам следует перейти к следующему шагу: придумать математическое доказательство правильности.
Математические доказательства правильности
Итак, нам нужно доказать, что наш жадный алгоритм верен: он выводит оптимальное решение (или, если есть несколько оптимальных решений, которые одинаково хороши, он выводит одно из них).
Основной принцип интуитивно понятен:
Принцип: если вы никогда не сделаете плохой выбор, все будет хорошо.
Жадные алгоритмы обычно включают в себя последовательность выбора. Основная стратегия доказательства состоит в том, что мы попытаемся доказать, что алгоритм никогда не делает неправильный выбор. Жадные алгоритмы не могут отказаться - как только они сделают выбор, они преданы и никогда не отменят этот выбор - поэтому важно, чтобы они никогда не делали плохой выбор.
Что считается хорошим выбором? Если есть единственное оптимальное решение, легко увидеть, что является хорошим выбором: любой выбор, идентичный тому, который сделан оптимальным решением. Другими словами, мы попытаемся доказать, что на любом этапе выполнения жадных алгоритмов последовательность выборов, сделанных алгоритмом, точно соответствует некоторому префиксу оптимального решения. Если существует несколько одинаково хороших оптимальных решений, хорошим выбором будет тот, который соответствует хотя бы одному из оптимумов. Другими словами, если последовательность выбора алгоритма до сих пор совпадает с префиксом одного из оптимальных решений, все в порядке (пока ничего не пошло не так).
Чтобы упростить жизнь и устранить отвлекающие факторы, давайте сосредоточимся на случае, когда нет связей: есть единственное, уникальное оптимальное решение. Все оборудование будет перенесено на тот случай, когда может быть несколько одинаково хороших оптимумов без каких-либо фундаментальных изменений, но вы должны быть немного осторожнее с техническими деталями. Начните с игнорирования этих деталей и сосредоточьте внимание на случае, когда оптимальное решение является уникальным; это поможет вам сосредоточиться на том, что важно.
Существует очень распространенный образец доказательства, который мы используем. Мы будем усердно работать, чтобы доказать следующее свойство алгоритма:
Утверждение: пусть будет решением, выводимым алгоритмом, и O будет оптимальным решением. Если S отличается от O , то мы можем подправить O , чтобы получить другое решение O * , который отличается от O и строго лучше , чем O .S О S О О О* О О
Обратите внимание, почему это полезно. Если утверждение верно, значит, алгоритм верен. Это в основном доказательство от противного. Либо такое же , как O , или она отличается. Если оно отличается, то мы можем найти другое решение O ∗, которое строго лучше, чем O - но это противоречие, поскольку мы определили O как оптимальное решение, и не может быть никакого решения, которое лучше этого. Поэтому мы вынуждены сделать вывод, что S не может отличаться от O ; S всегда должно быть равно OS О О* О О S О S О жадный алгоритм всегда выводит правильное решение. Если мы можем доказать утверждение выше, то мы доказали, что наш алгоритм правильный.
Хорошо. Так, как мы доказываем требование? Мы рассматриваем решение как вектор ( S 1 , … , S n ), который соответствует последовательности n выборов, сделанных алгоритмом, и аналогичным образом мы рассматриваем оптимальное решение O как вектор ( O 1 , … , о п ) , соответствующий последовательности вариантов , которые привели бы к O . Если S отличается от O , должен существовать некоторый индекс i, где S i ≠S ( S1, … , SN) N О ( O1, … , ОN) О S О я ; мы сосредоточимся на самых маленьких, таких как я . Затем мы настроим O ,немногоизменив O в i- й позиции, чтобы она соответствовала S i , т.е. мы настроим оптимальное решение O , изменив i- й вариант на выбор, выбранный жадным алгоритмом, и затем мы покажем, что это приводит к еще лучшему решению. В частности, мы определим O ∗ как что-то вродеSя≠ Oя я О О я Sя О я О*
за исключением того, что нам часто приходится слегка изменять части чтобы поддерживать глобальную согласованность. Часть стратегии доказательства включает в себя некоторый ум в правильном определении O ∗ . Тогда основной смысл доказательства состоит в том, чтобы как-то использовать факты об алгоритме и задачу показать, что O ∗ строго лучше OОя + 1, Oя + 2, … , ОN O∗ O∗ O ; вот где вам понадобится понимание проблем. В какой-то момент вам нужно погрузиться в детали вашей конкретной проблемы. Но это дает вам представление о структуре типичного доказательства корректности жадного алгоритма.
Простой пример: подмножество с максимальной суммой
Это может быть легче понять, если детально проработать простой пример. Давайте рассмотрим следующую проблему:
Входные данные: набор целых чисел, целое число k Выходные данные: набор S ⊆ U размера k , сумма которых максимально возможнаU k
S⊆U k
Есть естественный жадный алгоритм для этой проблемы:
Случайное тестирование показывает, что это всегда дает оптимальное решение, поэтому давайте формально докажем, что этот алгоритм правильный. Обратите внимание, что оптимальное решение уникально, поэтому нам не нужно беспокоиться о связях. Давайте докажем утверждение, изложенное выше:
источник
then we can tweak O to get another solution O∗ that is different from O and strictly better than O
смущает меня. Если существует несколько оптимальных решений, возможно, что ониS != O
оба будут оптимальными; мы можем настроить O, чтобы быть «больше похожим» на S (создавая O ∗), и в то же время быть таким же хорошим, как (неstrictly better than
) O.a single, unique optimal solution
. Поскольку этот вопрос касается проверки правильности любого жадного алгоритма, я хотел бы дать ответ для случаев, когда может существовать несколько оптимальных решений. Прошло много времени с тех пор, как я изучил все это, но разве этого недостаточно, чтобы доказать, что вы можете обменять каждый элемент O_i на любое оптимальное решение O, отличное от alg. решение S с S_i и до сих пор есть решение O ', которое не хуже, чем O?Я буду использовать следующий простой алгоритм сортировки в качестве примера:
Чтобы доказать правильность, я использую два шага.
Для первого пункта я выбрал подходящую функцию стоимости, для которой я могу показать, что алгоритм улучшает ее на каждом этапе.
Это доказывает, что алгоритм в конечном итоге завершается.
Количество инверсий в отсортированном списке равно 0. Если все пойдет хорошо, алгоритм уменьшит количество инверсий до 0. Нам нужно только показать, что он не застревает в локальном минимуме.
Я обычно доказываю это противоречием. Я предполагаю, что алгоритм остановлен, но решение не является правильным. В примере это означает, что список еще не отсортирован, но нет соседних элементов в неправильном порядке.
Это доказывает, что алгоритм останавливается только после сортировки списка. И, следовательно, мы сделали.
источник