Программирование головоломки - невозможно оптимизировать? - PullRequest
5 голосов
/ 06 января 2012

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

Например, в одной загадке вам дается сетка 3х3 чисел от 1 до 9 ниже:

123
456
789

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

123 -> 312
456    456
789    789

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

Я написал алгоритм грубой силы DFS для проверки всех возможных последовательностей ходов, хотя количество доступных ходов на каждом ходу увеличивается экспоненциально (примерно 12 ^ [текущий ход]), что делает его бесполезным.

Кажется, что BFS был бы оптимальным для поиска правильных ходов, но для этого потребовалось бы хранить сотни, если не тысячи копий сетки, чтобы вернуться назад


Я постоянно сталкиваюсь с такими проблемами. Оба алгоритма BFS и DFS используют слишком много памяти и времени, соответственно. Мне нужна помощь в оптимизации таких алгоритмов, чтобы они работали быстрее и эффективнее. Может быть, поможет распознать закономерности и соотношения чисел или дать логику алгоритма для достижения цели? (Я не знаю, что это повлечет за собой).

EDIT:

Мой исправленный алгоритм работает как шарм. Научиться нумеровать мои перестановки было необходимо. Спасибо всем!

Ответы [ 5 ]

9 голосов
/ 06 января 2012

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

Для задач DFS и BFS в целом может быть интересна методика, известная как Branch and Bound.Ограничительная часть может дать вам существенную выгоду в некоторых проблемах.Обрезка поддерева на одно поколение выше, чем с менее сложной границей, устраняет много новых ветвей в вашем дереве поиска (альтернативная формулировка: поскольку деревья растут экспоненциально с глубиной, важно сократить поиск раньше).

Для вашей конкретной проблемы,Я считаю, что оптимизации возможны.

Сначала давайте рассмотрим DFS.Я считаю, что все перестановки вашей доски достижимы из любой конфигурации платы.Как следствие.DFS может быть реализован без возврата (хотя я предполагаю, что вы это знали).Глубина только поиск?(РЕДАКТИРОВАТЬ: согласно Дэниелу Фишеру, это неправильно. Половина состояний достижима, хотя это не влияет на утверждение об отсутствии возврата, так как возврат не поможет вам достичь ваших недоступных состояний)

Но вы можетеобнаружите, что вы не хотите проходить через множество перестановок, просто чтобы обнаружить, что вы еще не решили проблему.Отступление может действительно помочь.Или ...

Подумайте о том, чтобы взглянуть на свою конечную цель.Магические квадраты имеют некоторые особые свойства, которые вы можете использовать, чтобы более тщательно выбирать операции.Например, поскольку количество строк и столбцов должно составлять 15, вы знаете, что 9, 8 и 7 не могут совместно использовать строки или столбцы друг с другом.Ни один не может 9 и 6. 6 не может идти с 8 и 1 или 7 и 2. 6 не может разделить столбец / строку с 5 и 4, даже если они суммируют до 15 из-за принципа «голубиного отверстия» (каждая строка / столбец содержит либо 98 или 7).Фактически, вы можете обнаружить, что ваша задача имеет уникальное решение, по модулю какой-то циклической перестановки во всех строках, во всех столбцах, в отражении и транспонировании.Требование к диагонали дополнительно ограничивает действительные решения.

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

Теперь у вас есть цель:

  1. Опишите состояние решения.
  2. Достигните одного из известных состояний решения с наименьшим количеством ходов.

Это принципиально иной подход, чем поиск различных перестановок, пока проблема не будет решена.Я бы попытался найти решение для динамического программирования.Для немного более легкой задачи динамического программирования, которая с инкрементными операциями переходит из начального состояния в целевое, взгляните на проблему расстояния редактирования Левенштейна.

4 голосов
/ 06 января 2012

Несколько замечаний в дополнение к хорошему ответу ccoakley и комментарию stubbscroll относительно конкретного примера и нескольких общих принципов.

Что касается замечания Стаббскролла, что у этой конкретной проблемы всего 9! = 362880 разных состояний:
Одним (довольно простым) способом кодирования перестановок в виде чисел является индексация перестановок с помощью лексикографического упорядочения. Например

0 1 2 3  -->  0
0 1 3 2  -->  1
0 2 1 3  -->  2
...
1 0 2 3  -->  6
1 0 3 2  -->  7
...
3 1 2 0  --> 21
3 2 0 1  --> 22
3 2 1 0  --> 23

Хитрость в написании индекса в факториальной базе,

n = a_1 * 1! + a_2 * 2! + a_3 * 3! + a_4 * 4! + ...

, где 0 <= a_k <= k. Если у вас есть символы s, индексы варьируются от 0 до s! -1, поэтому у вас есть коэффициенты s-1 в разложении факториальной базы n, (a_1,a_2,...,a_(s-1)). Тогда перестановка с индексом n определяется следующим образом

 for i = 1 to s-1
    the i-th symbol becomes the (a_(s-i)+1)-th smallest unused symbol
 the last symbol is the left over one

Поскольку это не очень понятно, пример. Скажем, мы ищем перестановку с индексом 4231 {1,2,3,4,5,6,7,8}. Сначала расширим 4231 в факторной базе

4231 = 1 + 2*2115 :  a_1 = 1
2115 = 0 + 3* 705 :  a_2 = 0
 705 = 1 + 4* 176 :  a_3 = 1
 176 = 1 + 5*  35 :  a_4 = 1
  35 = 5 + 6*   5 :  a_5 = 5
   5 = 5 + 7*   0 :  a_6 = 5

все последующие коэффициенты (здесь только a_7) равны 0. Лучше следовать записи a_i в обратном порядке (a_7, a_6, ... a_1), поэтому

 coefficients      symbols       choice
0,5,5,1,1,0,1  1,2,3,4,5,6,7,8     1
 5,5,1,1,0,1    2,3,4,5,6,7,8      7
  5,1,1,0,1      2,3,4,5,6,8       8
   1,1,0,1        2,3,4,5,6        3
    1,0,1          2,4,5,6         4
     0,1            2,5,6          2
      1              5,6           6
      -               5            5

Результат: 17834265.

Найдите индекс 246351:

symbols     count     perm    index(head)
1,2,3,4,5,6   6   2,4,6,3,5,1    1         a_5 = 1
 1,3,4,5,6    5    4,6,3,5,1     2         a_4 = 2
  1,3,5,6     4     6,3,5,1      3         a_3 = 3
   1,3,5      3      3,5,1       1         a_2 = 1
    1,5       2       5,1        1         a_1 = 1

Индекс `1 * 5! + 2 * 4! + 3 * 3! + 1 * 2! + 1 * 1! = 187.

Так что теперь у нас есть довольно простой способ преобразования между перестановками и их индексами. Преобразование не очень быстрое (O (s ^ 2)), но вы получаете быстрое и простое сравнение и поиск (я видел состояние раньше?). Будет ли это усиление, предстоит решить в каждом случае.

Теперь, для данного конкретного случая, у нас есть некоторые дополнительные ограничения, уменьшающие пространство поиска.

  • Каждый ход представляет собой циклическую перестановку из трех элементов, таким образом, даже перестановка.

Следовательно, все комбинации таких ходов также являются перестановками, что означает, что половина возможных состояний недоступна. Нам осталось (максимум) 9! / 2 = 181440 достижимых состояний. Индексирование даже перестановок с помощью лексикографического упорядочения лишь немного сложнее. Важным моментом является то, что перестановка является четной тогда и только тогда, когда сумма коэффициентов a_k в разложении факторного основания его индекса четна.

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

В примере задачи мы имеем еще одну приятную ситуацию, когда 5 уже находится в правильном месте, и что оптимальное решение никогда не сдвинет его с места. Таким образом, нам нужно рассмотреть только четные перестановки из 8 символов, сокращая пространство поиска до 8! / 2 = 20160 возможных состояний. (Хотя это не очевидно.)

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

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

3 голосов
/ 06 января 2012

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

На самом деле, есть решение, которое потребует всего 4 вращения.

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

1 голос
/ 06 января 2012

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

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

Вам может показаться интересным чтение о «полноте NP», поскольку это лишь небольшой уголпроблема, но хорошо изученная.

1 голос
/ 06 января 2012

Попробуйте использовать A *.Вы можете использовать эвристику Манхэттенского расстояния от того места, где числа находятся, где они должны быть.Я предполагаю, что у вас уже есть магический квадрат в качестве целевого состояния.

...