Как я могу улучшить этот алгоритм для решения модифицированной головоломки Почтовые марки? - PullRequest
24 голосов
/ 24 сентября 2010

Задача сына Дартса был соревнованием по соревнованиям по программированию Аль Циммермана , которое закончилось 20 июня 2010 года:

  • Предположим, что у вас есть дартс, который разделен на R регионов.Каждая область дартс имеет положительное целое значение, связанное с ним.Далее предположим, что у вас есть D-дротики и вы бросаете каждый из них в дартс.Каждый дротик либо приземляется в одной из областей R на доске, либо пропускает доску.Ваша оценка - это сумма значений для регионов, в которых приземляются дротики.Дротик, который не попадает на игровое поле, не влияет на ваш счет.Если несколько дротиков приземляются в одном регионе, вы накапливаете значение для этого региона несколько раз.

  • Например, предположим, что R = 5, что у областей дартс есть значения (1, 2, 4, 7, 11), и что D = 3. Если ваши три дротика приземляются в регионах 2, 4 и 11, вы получаете 17 очков.Если один дротик не попадает на игровое поле, а два других в регионе 7 набирают 14 очков.

  • Проблема дротиков заключается в следующем: для данных R и D определите, какие значения должны быть связаныс R областями дротика, чтобы максимизировать наименьшее количество очков, недостижимое путем броска дротиков D.

    D = number of darts    R = number of dartboard regions
        3                      1 through 40
        4                      1 through 30
        5                      1 through 20
        6                      1 through 10
    

===================================================================================

ИСПОЛЬЗОВАННЫЙ ОСНОВНОЙ АЛГОРИТМ (объяснено для D = 3)

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

     ____ ____ 
    |    |    |
    |  0 |  1 |
    |____|____|
    
  • Я генерирую scratch массив, который показывает, что все итоги достижимы, используя результаты дротиков в массиве результатов, в три броска.Подчеркнутые элементы - это те, которые использовались для создания нуля .

    0 = 0 + 0 + 0
    1 = 1 + 0 + 0
    2 = 1 + 1 + 0
    3 = 1 + 1 + 1
     ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____
    | *  | *  | *  | *  |    |    |    |    |    |    |    |    |    |
    |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
    |__-_|__-_|____|____|____|____|____|____|____|____|____|____|____|
    
  • Теперь кандидатами на следующий элемент в массиве результатов являются 2 , 3 или 4 .

    • 2 = элемент на один больше, чем текущий наибольший элемент

    • 4 = малый недостижимый элемент

  • Я пробую каждого из этих кандидатов по очереди иувидеть то, что является максимумом самых маленьких недостижимых элементов в каждом случае.Например,

    (0, 1, 2 )

     ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 
    | *  | *  | *  | *  | *  | *  | *  |    |    |    |    |    |    |
    |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
    |__-_|__-_|__-_|____|____|____|____|____|____|____|____|____|____|
    

    (0, 1, 3 )

     ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 
    | *  | *  | *  | *  | *  | *  | *  | *  |    | *  |    |    |    |
    |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
    |__-_|__-_|____|__-_|____|____|____|____|____|____|____|____|____|
    

    (0, 1, 4 )

     ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 
    | *  | *  | *  | *  | *  | *  | *  |    | *  | *  |    |    | *  |
    |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
    |__-_|__-_|____|____|__-_|____|____|____|____|____|____|____|____|
    
  • max (7, 8, 7) = 8.Итак, 3 выбран в качестве следующего элемента.

  • Если предположить, что была связь, например, если мне пришлось выбирать между (0, 1, 2 ) и (0, 1, 4 ), чтобы разорвать связь, я бы посчитал число достижимых чисел, которое больше в случае (0, 1, 4 ).Итак, я бы выбрал (0, 1, 4 ), а не (0, 1, 2 ).

  • Но в этом случае(0, 1, 3 ) является победителем, и набор результатов выглядит следующим образом.Затем я повторяю шаги, пока не получу 41 элемент.

     ____ ____ ____
    |    |    |    |
    |  0 |  1 |  3 |
    |____|____|____|
    
  • Алгоритм является жадным в том смысле, что он предполагает, что (решение для R = 20) является подмножествомрешение для R = 30).Итак, я не вычисляю результаты для каждого значения R, но я вычисляю результаты для каждого значения D. Итак, для D = 3 я рассчитываю результат для R = 40, а затем беру первые 30 элементов результата для R= 30, например.

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

  • Чтобы иметь возможность работать лучше, чем перебор, алгоритм исключает некоторые кандидаты в массив результатов.Например, в случае, изображенном на диаграмме ниже (для массива (0, 1, 4) result ), я рассматриваю только 5, 6 и 7 в качестве кандидатов на следующий элемент и исключаю 2 и 3хотя они до сих пор не используются.Это предположение может дать мне неоптимальные результаты в некоторых случаях, но оно сокращает многие вычисления, когда scratch увеличивается до тысяч элементов.

     ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 
    | *  | *  | *  | *  | *  | *  | *  |    | *  | *  |    |    | *  |
    |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
    |__-_|__-_|____|____|__-_|____|____|____|____|____|____|____|____|
    

УЛУЧШЕНИЯ К АЛГОРИТМУ

  • Поскольку это не давало слишком хороших результатов, я попытался создать наборы результатов для каждого значения D. Например, вместо выбора лучшего следующего элемента для результат , я также могу выбрать второй или третий лучший элемент.Таким образом, за 39 шагов (до 41 элемента в результате) я мог бы сгенерировать около 3 ^ 39 (для каждого выбора у меня может быть либо лучший, либо второй лучший, либо третий лучший элемент), что слишком много.Итак, я решил выбрать не более одной секунды и не более одной трети лучших.Это привело к уменьшению количества дел примерно до 40 C 1 * 39 C 1 = 577980 результатов.Более того, это привело бы к ОГРОМНОМУ количеству случаев для более высоких значений R (для более высоких значений D).

  • Из этих полученных ~ 6E5 результатов я вычисляю минимальноенедостижимый элемент для каждого значения R от 1 до 40. Таким образом, каждое из значений R получает возможность выбирать лучшее из этих значений 6E5.

ПРОБЛЕМЫ

  • Этот алгоритм не дает лучших результатов наборов или даже близко.

  • Я даже пошел к четвертому и пятому лучшим вариантам для D = 3,и они не дали каких-либо серьезных улучшений в результатах.Итак, я не знал, где мне настроить мой алгоритм.

Где все можно настроить этот алгоритм, чтобы получить лучшие результаты?

Есть ли явные ошибки, которые допускает алгоритм?

Любые комментарии / предложения приветствуются.

Был еще один вопрос на ту же тему здесь .Меня больше интересует, где можно улучшить мой алгоритм.

Ответы [ 4 ]

3 голосов
/ 27 сентября 2010

Я действительно участвовал в этом конкурсе, как вы заметили, я занял 100-е место в общем с набранными 87,00 очками.Я на самом деле использовал ваш метод, потому что понял, что создание «разумного» решения для каждой проблемы было первым препятствием, которое нужно преодолеть.В то время, когда я набрал его, сгенерированных очков было достаточно, чтобы поднять меня до 94 баллов, но по мере того, как лучшие игроки улучшили свои баллы, это число быстро упало до 75.

Первая и самая легкая вещь, которую вы можете сделатьэто понять, что ваша программа запускается в одно мгновение, и если это не так, вы должны потратить время на оптимизацию дерьма из этого в первую очередь.Как только он запустится за достаточно короткое время, вы можете создать любой возможный набор для D = 3, R <= 5 в кратчайшие сроки.Затем вы можете использовать наборы на R = 5 в качестве начальных значений для вашего жадного алгоритма.Теперь вместо одного жадного решения у вас есть несколько тысяч, и вам просто нужно отслеживать самое высокое значение, генерируемое на каждом уровне R, и значения, которые его создают.Это достаточно легко сделать, и это повысит ваш счет выше 80.

Я потратил почти месяц на оптимизацию функции, которая может тестировать любой набор случайных входов, чтобы я мог накачать свой генератор семян доR = 10 и запустите его примерно за 8 часов на одном ядре.Это гарантировало наилучшее возможное решение для «D = 3», «R <= 10» и гораздо лучшие ответы при <code>R > 10, чем у меня с исходным жадным результатом.Это привело к тому, что мой результат был близок к тому, где он закончился, после запуска R как можно выше для каждого D, и при этом возможность запуска программы в течение одного дня.Я смог достичь R = 9 для D = 4, R = 8 для D = 5 и R = 8 для D = 6.

После того, как они были выполнены, я рассчитал, что не смогуувеличьте R на 1 для любого D по сравнению с только что перечисленными номерами и сделайте так, чтобы он завершил свое выполнение в моей жизни.Очевидно, пришло время искать новую технику.Я подумал, что проделал отличную работу во внешнем интерфейсе, протестировав каждый возможный начальный сегмент, так почему бы не перенести часть этого на внутренний, протестировав более глубокие наборы результатов для каждого из моих начальных сегментов.Вместо того, чтобы получить лучший следующий результат на том же уровне, я выполнил двухуровневый просмотр вперед и выбрал лучшее следующее значение из двух уровней.Например, вы всегда начинаете с 1, затем перечисляете все значения для R = 2 (2, 3, 4), а затем для каждого из них оцениваете все возможные значения для R = 3.Так что 2 (3, 4, 5, 6, 7), 3 (4, 5, 6, 7, 8), 4 (5, 6, 7).Возьмите наибольшую из всех этих оценок, а затем выберите значение R = 2, которое содержит наибольшее значение для R = 3.Это потребовало намного больше времени на обработку и потребовало от меня уменьшения максимального значения R, которое я мог использовать для его заполнения, но это дало лучшие результаты для более глубоких поисков.

В этот момент я отказался от жадных подходов ииспользовал этот конкурс, чтобы расширить свои знания AI.Я пытался использовать различные техники Монте-Карло, а также базовый генетический алгоритм.Я многое узнал о Монте-Карло и, отыскивая некоторых из лучших участников этого конкурса, нашел публикации по оптимизации критериев отбора в Монте-Карло, которые были за пределами моего понимания.Я хотел бы помочь вам в этом, но я уверен, что могу утверждать, что преодоление 90 очков с жадным подходом очень маловероятно в моей жизни.Мне было бы интересно узнать, насколько лучше получились бы ответы, если бы они были многопоточными и на них было наложено много энергии.

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

РЕДАКТИРОВАТЬ: код, первоначально размещенный на сервере, который был заброшен.Пожалуйста, отправьте сообщение, если хотите, чтобы оно было опубликовано повторно.

1 голос
/ 08 октября 2010

Максимум самого маленького недостижимого элемента хорошо искать только на последней итерации.Лучшей первичной подцелью является количество достижимых элементов с заданным количеством дротиков.Одной интересной подцелью может быть

Nae * (Rt - Rc) / Rt + Msue, where
  • Nae - количество достижимых элементов (с заданным количеством дротиков)
  • Rt - общее количество доступных областей
  • Rc -используемые в настоящее время регионы (текущий уровень рекурсии)
  • Msue - максимум наименьшего недостижимого элемента

Почему Nae более ценен, чем Msue на ранних итерациях?Чем больше достижимых элементов у нас есть на раннем этапе, тем более последовательные элементы будут более пригодными для использования, будут производить больше комбинаций и достигать еще более достижимых элементов.С взрывом Nae появляется высокая вероятность того, что Msue также поднимется до хорошего уровня.Однако на поздних итерациях Msue становится все более важным, и новые элементы все чаще используются для «закрытия отверстий», в надежде, что последнее отверстие для подключения будет как можно дальше.

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

1 голос
/ 27 сентября 2010

Спасибо за очень интересный вопрос!Я потратил несколько минут, чтобы понять вопрос.

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

Во-первых,Наблюдение (как вы правильно сделали), одна из областей должна быть 1, в противном случае 1 было бы недостижимо.

Во-вторых, поскольку мы пытаемся МАКСИМИЗИРОВАТЬ недостижимую оценку, нет смысла повторять значения областей.

Итак, это дает вырожденное (но не оптимальное) решение: 1, 2, 3, ... R

Значение целевой функции этого вырожденного решения равно: D * R + 1

Например, если у вас D = 4 дротика, и вы раскрашиваете дартс со счетами 1, 2. .. 40, то все значения от 1 до 160 достижимы.161 недостижимо.

Очевидно, что это решение не является оптимальным, и оптимальное решение будет включать в себя, возможно, некоторые степени 2 и определенно некоторые мысли.

Теперь для обозначения:

Пусть f (X, D, {Y_1..Y_R}) =

  • 1, если оценка X достижима при использовании D-дартс на мишени с диапазонами Y_1 ...Y_R
  • 0, если недостижимо

Как пример, рассмотренный ранее.f (160,4, {1..40}) = 1 и f (161,4, {1..40}) = 0

Значение целевой функции головоломки можно обозначить как:

g (D, (Y_1..Y_R}) = Мин. X | f (X, D, {Y_1..Y_R}) = 0

По ранее замечанию 1 можно предположить, чтоY_1 = 1.

Также рекурсивная формулировка функции f может быть следующей:

f (X, D, {1..Y_R} = 1, если:

  • X = 0 или
  • f (X-Y_j, D-1, {1..Y_R}) = 1 для некоторого j

[Продолжитработайте над этим и публикуйте больше по мере его разработки.]

0 голосов
/ 04 октября 2010

После того, как вы переборщили несколько человек, вы можете увидеть некоторые паттерны для информирования об эвристических поисках. Например, у многих топовых решений есть шаблон, подобный этому, для D = 3, R = 40: серия небольших увеличений, серия больших увеличений, затем 2-кратный скачок с последующим коротким циклом небольшого увеличения.

По крайней мере, он говорит вам не придерживаться идеи подмножества, где значения 3x30, например, являются подмножеством 3x40.

alt text

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...