Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Алгоритмы > Генерация кроссвордов


Автор: Kuvaldis 8.8.2006, 10:38
Доброго всем времени суток!!!smile 

До учебы еще целый месяц, делать нечего. Поэтому учу С++ по книге Дейтела. А в ней в разряде "головоломные" раздела на строки предложена задача на генерацию кроссворда (заполненный словами, желательно симметричный) Ну, очевидное решение у меня есть - простой backtracking. Но, по-моему, очень (!) это неэффективно. В сети исходников нет (вроде искал хорошо), но есть куча готовых программ. Может кто, грешным делом, занимался этой проблемой? Подскажите хотя бы идею 

Заранее спасибо   

Автор: Kuvaldis 14.8.2006, 21:21
Уважаемые форумчане! Я конечно понимаю, что спасение утопающих - дело рук самих утопающих. Но неужели мой вопрос окажется неуслышенным?

Автор: Akina 14.8.2006, 21:37
Я не понял - речь о генерации сетки, заполнении готовой сетки, both?

Автор: Kuvaldis 14.8.2006, 23:55
Akina
Цитата

Я не понял - речь о генерации сетки, заполнении готовой сетки, both?

Генерация готовой сетки по словарю (т.е. both)

как я понял в условии задачи, входные параметры = это только размер кроссворда,  а дальше генерится заполненная сетка со словами

Автор: nostromo 15.8.2006, 09:47
Я, пожалуй, сделал бы следующую вариацию бэктрекинга.

Сначала, создаем базу данных с таблицей, содержащей поля: слово, длина слова, 1-я буква, 2-я буква, ...., 20-я буква. По всем полям  создаем индексы для быстрого поиска.
Запросы типа "найти все слова длины 6 где 2-я буква=А и 5-я буква=Ц" должны выполняться весьма быстро.
Таблица одна, скорее всего целитком влезет в память, поэтому полноценную СУБД задействовать, возможно, особого смысла нет и можно ограничиться самодельной программной реализацией.

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

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

Автор: Kuvaldis 15.8.2006, 14:11
nostromo
Во-первых, спасибо за ответ и за участие (к сожалению, пока 100 постов нет smile , но я запомню )
во-вторых, получается, что оптимальным вариантом является сначала генерация сетки, а потом ее заполнение? 
Совместить эти процессы никак не получится? или это нерационально?
Тем более если словарь не очень большой

Автор: Kernigan 25.8.2006, 17:18
Хотел бы тебе чем-то помощь, но, к сожалению, подобными вещами не занимался, хотя возможность была. 
Надеюсь, что до конца каникул и до начала учебного года, ты всёже найдёшь правильное решение.

Автор: Kuvaldis 25.8.2006, 18:20
Вроде бы, нашел толковый вариант

Код

Для очередного слова в сетке (желательно начинать с длинных)

фаза Do
* определяешь маску (какие буквы уже определены)
* получаешь курсор выборки из словаря по данной маске и запоминаешь его значение
* если обломились — производишь откат к предыдущему слову
* вставляешь выбранное из словаря слово и переходишь к следующему

фаза Redo (когда был откат)
* переводишь курсор вперед
* если обломились (или вернулись на первое запомненное значение) — производишь откат к предыдущему слову
* вставляешь выбранное из словаря слово и переходишь к следующему

Чтобы внести элемент случайности, обращение к словарю сделаем с изюминкой.

Пусть есть таблица слов длиной N. Курсор — это индекс i в этой таблице.
Получение курсора:
* взять число i0 = random(0..N-1), i1 = (i0-1)%N
* взять число i; for(i=i0; i != i1 && !match_mask(word[i]); i=(i+1)%N) {}
* если !match_mask(word[i]), то курсор получить не удалось
* иначе cursor=i
Переход к следующему:
* то же самое, только i0 = cursor+1, i1 = cursor

Для случаев, когда в маске зафиксирован префикс (первые несколько букв) — диапазон поиска можно сократить.

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

Как я уже говорил, удобно разбить словарь по длине слов.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)