Можете ли вы победить британскую разведку? (Nonogram Solver)


20

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

Что такое нонограмма?

Nonogram Puzzle

Правила просты. У вас есть сетка квадратов, которые должны быть либо заполнены черным, либо оставить пустым. Рядом с каждой строкой сетки указаны длины чёрных квадратов в этой строке. Над каждым столбцом указаны длины чёрных квадратов в этом столбце. Ваша цель - найти все черные квадраты. В этом типе головоломки числа - форма дискретной томографии, которая измеряет, сколько непрерывных линий заполненных квадратов есть в любой данной строке или столбце. Например, подсказка «4 8 3» будет означать, что имеются наборы из четырех, восьми и трех заполненных квадратов в указанном порядке, по крайней мере, с одним пустым квадратом между последовательными группами. [ 1 ] [ 2 ]

Таким образом, решение вышеупомянутого Nonogram будет:

Решена нонограмма

Детали реализации

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

Эта проблема очень сложна с точки зрения алгоритма (np-complete) в том смысле, что не существует полностью эффективного решения, и поэтому вы не будете наказаны за то, что не можете решить более крупные задачи, хотя ваш ответ будет в значительной степени вознагражден, если он способен обрабатывать большие дела (см. бонус). В качестве теста мое решение работает примерно до 25x25 в течение 5-10 секунд. Чтобы обеспечить гибкость различных языков, достаточно хороши решения, которые занимают менее 5 минут для неграммы 25x25.

Вы можете принять загадку всегда в квадрате NxN nonogram.

Вы можете использовать этот онлайн-пазл, чтобы проверить свои решения.

счет

Вы, конечно, можете свободно использовать любой язык, который хотите, и поскольку это кодовый гольф, записи будут отсортированы в следующем порядке: accuracy -> length of code -> speed.однако, не расстраивайтесь по языкам игры в гольф, ответы на всех языках, которые показывают попытки игры в гольф интересным образом будет проголосовано!

бонус

Я на самом деле узнал о Кроссвордах от криптографической Рождественской открытки выпущенной британской разведки здесь . Первая часть была в основном массивной Нонограммой 25х25. Если ваше решение способно решить эту проблему, вы получите слава :)

Чтобы упростить вашу жизнь с точки зрения ввода данных, я представил, как я представлял данные для этой конкретной головоломки для свободного использования. Первые 25 строк - это строки-подсказки, за которыми следует разделительная строка '-', за которыми следуют 25 строк столбцов, за которыми следует строка-разделитель '#', а затем представление сетки с заполненными квадратными подсказками.

7 3 1 1 7
1 1 2 2 1 1
1 3 1 3 1 1 3 1
1 3 1 1 6 1 3 1
1 3 1 5 2 1 3 1
1 1 2 1 1
7 1 1 1 1 1 7
3 3
1 2 3 1 1 3 1 1 2
1 1 3 2 1 1
4 1 4 2 1 2
1 1 1 1 1 4 1 3
2 1 1 1 2 5
3 2 2 6 3 1
1 9 1 1 2 1
2 1 2 2 3 1
3 1 1 1 1 5 1
1 2 2 5
7 1 2 1 1 1 3
1 1 2 1 2 2 1
1 3 1 4 5 1
1 3 1 3 10 2
1 3 1 1 6 6
1 1 2 1 1 2
7 2 1 2 5
-
7 2 1 1 7
1 1 2 2 1 1
1 3 1 3 1 3 1 3 1
1 3 1 1 5 1 3 1
1 3 1 1 4 1 3 1
1 1 1 2 1 1
7 1 1 1 1 1 7
1 1 3
2 1 2 1 8 2 1
2 2 1 2 1 1 1 2
1 7 3 2 1
1 2 3 1 1 1 1 1
4 1 1 2 6
3 3 1 1 1 3 1
1 2 5 2 2
2 2 1 1 1 1 1 2 1
1 3 3 2 1 8 1
6 2 1
7 1 4 1 1 3
1 1 1 1 4
1 3 1 3 7 1
1 3 1 1 1 2 1 1 4
1 3 1 4 3 3
1 1 2 2 2 6 1
7 1 3 2 1 1
#
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

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

([[7, 3, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 1, 3, 1],
  [1, 3, 1, 1, 6, 1, 3, 1],
  [1, 3, 1, 5, 2, 1, 3, 1],
  [1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [3, 3],
  [1, 2, 3, 1, 1, 3, 1, 1, 2],
  [1, 1, 3, 2, 1, 1],
  [4, 1, 4, 2, 1, 2],
  [1, 1, 1, 1, 1, 4, 1, 3],
  [2, 1, 1, 1, 2, 5],
  [3, 2, 2, 6, 3, 1],
  [1, 9, 1, 1, 2, 1],
  [2, 1, 2, 2, 3, 1],
  [3, 1, 1, 1, 1, 5, 1],
  [1, 2, 2, 5],
  [7, 1, 2, 1, 1, 1, 3],
  [1, 1, 2, 1, 2, 2, 1],
  [1, 3, 1, 4, 5, 1],
  [1, 3, 1, 3, 10, 2],
  [1, 3, 1, 1, 6, 6],
  [1, 1, 2, 1, 1, 2],
  [7, 2, 1, 2, 5]],
 [[7, 2, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 3, 1, 3, 1],
  [1, 3, 1, 1, 5, 1, 3, 1],
  [1, 3, 1, 1, 4, 1, 3, 1],
  [1, 1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [1, 1, 3],
  [2, 1, 2, 1, 8, 2, 1],
  [2, 2, 1, 2, 1, 1, 1, 2],
  [1, 7, 3, 2, 1],
  [1, 2, 3, 1, 1, 1, 1, 1],
  [4, 1, 1, 2, 6],
  [3, 3, 1, 1, 1, 3, 1],
  [1, 2, 5, 2, 2],
  [2, 2, 1, 1, 1, 1, 1, 2, 1],
  [1, 3, 3, 2, 1, 8, 1],
  [6, 2, 1],
  [7, 1, 4, 1, 1, 3],
  [1, 1, 1, 1, 4],
  [1, 3, 1, 3, 7, 1],
  [1, 3, 1, 1, 1, 2, 1, 1, 4],
  [1, 3, 1, 4, 3, 3],
  [1, 1, 2, 2, 2, 6, 1],
  [7, 1, 3, 2, 1, 1]])

К сожалению, мой сайт не работает, но раньше у него был достаточно быстрый решатель Nonogram; 5-10 минут звучит излишне.
Нил


1
@dwana Вам не нужно беспокоиться о неразрешимых случаях. Что касается случайного ответа, на нонограмме 25x25 у вас есть 2 ^ 625 возможных конфигураций. В контексте, это более чем в два раза больше атомов в известной вселенной (то есть если бы вы использовали каждый атом вселенной как бит, у вас все равно не было бы достаточно места для хранения возможностей). С точки зрения времени, если вам потребуется нано секунда (щедро), чтобы проверить правильность каждой конфигурации, потребуется 7 жизней юниверса, чтобы код завершил работу :)
gowrath

1
Ты для выяснения неразрешимых дел. (+ У меня есть волшебный компьютер, который подтверждает ответ в течение ~ 2.1546362E-186 секунд)
dwana

1
Ваш CSV не имеет квадратных подсказок. Вот некоторые JS, чтобы сгенерировать их:s=[].fill([].fill(0,0,25),0,25);s[3][3]=s[3][4]=s3[3][12]=s3[3][13]=s3[3][21]=s[8][6]=s[8][7]=s[8][10]=s[8][14]=s[8][15]=s[8][18]=s[16][6]=s[16][11]=s[16][16]=s[16][20]=s[21][3]=s[21][4]=s[21][9]=s[21][10]=s[21][15]=s[21][20]=s[21][21]=1;
Тит

Ответы:


5

Brachylog , 70 69 байт

[R:C]hlL~l:L:1f=.:3aR,.z:3aC,
tL,?he##ElL,E:2a
.<2,_1<
@b:4f:la
e.h1,

Для этого требуется список из двух списков (сначала указатели строк, а затем столбцы). Каждый индикатор представляет собой список (для ситуаций, как [3,1]в одной строке).

Эта версия занимает около 3 минут, чтобы решить 5 на 5 пример задачи.

Гораздо более эффективная версия, 91 байт

[R:C]hlL~l:L:1f:Cz:3az:Rz:3a=.:4aR,.z:4aC,
tL,?he##ElL,E:2a
.<2,_1<
:+a#=,?h
@b:5f:la
e.h1,

Попробуйте онлайн!

Это не полная грубая сила: единственное отличие состоит в том, что она накладывает ограничения на значения ячеек, так что число 1 в каждой строке и столбце соответствует числам, указанным в качестве индикаторов на входе. Единственная часть грубой силы состоит в том, чтобы найти одну сетку с теми ограничениями, для которых «блоки» 1 соответствуют тому, что задано в качестве индикации.

Это займет около 0,05 секунд на 5 на 5 примере испытания. Это все еще слишком медленно для бонусного случая, поскольку я понятия не имею, как выразить блоки единиц, разделенные одним или несколькими нулями, в терминах ограничений.

объяснение

Я объясню ниже версию 93 байтов. Единственное различие между ними заключается в обращении к предикату 3, которого нет в 70-байтовой версии, и в нумерации предикатов (поскольку их на один меньше).

  • Основной предикат:

    [R:C]     Input = [R, C]
    hlL       The length of R is L
    ~l        Create a list of length L
    :L:1f     Each element of that list is a sublist of length L with cells 0 or 1 (Pred 1)
              %%% Part unique to the 93 bytes version
    :Cz       Zip the rows of the list of lists with C
    :3a       The sum of 1s in each row is equal to the sum of the indicators (Pred 3)
    z         Transpose
    :Rz       Zip the columns of the list of lists with R
    :3a       The sum of 1s in each column is equal to the sum of the indicators (Pred 3)
              %%%
    =.        Assign values to the cells of the list of lists which satisfy the constraints
    :4aR,     The blocks of 1s must match the indicators on rows
    .z        Transpose
    :4aC,     The blocks of 1s must match the indicators on columns
    
  • Предикат 1: заставляет строки иметь определенную длину, и каждая ячейка равна 0 или 1.

    tL,       L is the length given as second element of the input
    ?he       Take an element from the list
    ##ElL,    That element E is itself a list of length L
    E:2a      The elements of E are 0s and 1s (Pred 2)
    
  • Предикат 2: ограничение переменной должно быть 0 или 1

    .<2,      Input = Output < 2
    _1<       Output > -1
    
  • Предикат 3: сумма 1 в списке должна быть равна сумме индикаторов (например, если индикатор [3: 1], то в списке должна быть сумма 4)

    :+a       Sum the elements of the list and sum the indicator
    #=,       Both sums must be equal
    ?h        Output is the list
    
  • Предикат 4: Убедитесь, что блоки единиц соответствуют индикатору

    @b        Split the list in blocks of the same value
    :5f       Find all blocks of 1s (Pred 5)
    :la       The list of lengths of the blocks results in the indicator (given as output)
    
  • Предикат 5: Истинно для блоков 1 с, иначе ложно

    e.        Output is an element of the input
      h1,     Its first value is 1
    

Чувствуется как идеальный инструмент для работы. С нетерпением жду объяснения.
Emigna

@Fatalize Это фантастика, я ждал, чтобы кто-нибудь использовал язык пролога, чтобы сделать это. Вы пробовали это с делом 25x25? Я ввел данные для вас уже
gowrath

@Gowrath Я запусту это на своем компьютере сегодня днем, посмотрим, что произойдет.
Fatalize

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

@gowrath Время ожидания на TIO, но я буду запускать его на автономном переводчике прямо на моем компьютере.
Fatalize

9

Haskell, 242 230 201 199 177 163 160 149 131 байт

import Data.Lists
m=map
a#b=[x|x<-m(chunk$length b).mapM id$[0,1]<$(a>>b),g x==a,g(transpose x)==b]
g=m$list[0]id.m sum.wordsBy(<1)

Наконец, до 200 байт, кредит @Bergi. Огромное спасибо @nimi за помощь почти вдвое уменьшить размер.

Вау. Почти вдвое меньше, отчасти из-за меня, но в основном из-за @nimi.

Волшебная функция есть (#). Находит все решения данной нонограммы.

Это может решить все случаи, но может быть очень медленным, так как его сложность составляет около O(2^(len a * len b)). Быстрый тест показал 86 ГБ, выделенных для 5x5 nonogram.

Интересный факт: он работает для всех неграмм, а не только для квадратных.


Как это устроено:

  • a#b: Даны списки списков целых чисел, которые представляют количество квадратов, генерируют все сетки ( map(chunk$length b).mapM id$a>>b>>[[0,1]]) и фильтруют результаты, чтобы сохранить только действительные.
  • g: Учитывая потенциальную нонограмму, она суммирует пробеги 1 по горизонтали.

Вы имеете в виду O (2 ^ (len a * len b)), а не O ((len a * len b) ^ 2).
Андерс Касеорг

@AndersKaseorg Верно. Держите миллион, который я случайно там подразумевал. : D
ThreeFx

1
Еще несколько байтов: m(chunk$l b)иreplicate(l$a>>b)
Берги

@ThreeFx 86GB: O ... Кстати, не могли бы вы кратко объяснить, как это скомпилировать? Я только начал изучать haskell, и это дает ошибки с ghc. Хочу проверить это :)
gowrath

1
import Data.Listsдостаточно, потому что он реэкспортирует Data.Listи Data.List.Split.
Ними

4

Pyth, 91 72 71 байт

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb

Программа , которая принимает данные из списка формы , [size, [horizontal clues], [vertical clues]]где каждый ключ представляет собой список целых чисел (пустые ключи являются пустым списком []), и печатает каждое решение, перевод строка разделена, в виде бинарной решетки , где 1затенен и 0является незатушеванным ,

Это грубая сила, примерно так O(2^n^2). Это займет очень много времени для больших головоломок, но разрешит любой произвольный размер, если будет достаточно времени.

Попробуйте онлайн

Как это устроено

Программа генерирует каждый возможный макет, беря повторяющееся декартово произведение [0, 1]с длиной, равной size^2. Затем это разделяется на куски, давая список для каждой горизонтальной линии. Каждая строка кодируется по длине серии, фильтруется по наличию 1и выравнивается, оставляя ключ к этой строке. Это тогда проверено против ввода. Вышеописанный процесс повторяется для транспонирования кусков, проверки вертикальных линий. Если есть совпадение, каждый фрагмент объединяется, и объединенные фрагменты объединяются на новых строках и печатаются неявно с последующим переводом строки.

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb  Program. Input: Q
                            hQ                                           Q[0], size
                           ^  2                                          Square
                        ,01                                              [0, 1]
                       ^                                                 Cartesian product
                      V                                     )            For N in the Cartesian product:
                                 cNhQ                                    Split N into Q[0] chunks
                               =T                                        Assign that to T
                                     =Y1                                 Y=1
                                        VhQ                              For H in range [0, Q[0]-1]:
D:GHd                                                                     def :(G, H, d)
                   rH8                                                     Run-length-encode(H)
               f.)T                                                        Filter by presence of 1 in character part
            .nC                                                            Transpose and flatten, giving the clue
       @@QdG                                                               Q[d][G], the relevant input clue
     Rq                                                                    Return clue==input clue
                                               :H@TH1                     :(H, T, 1)
                                                     :H@CTH2              :(H, transpose(T), 2)
                                           =*Y*                           Y=Y*product of above two
                                                             IY           If Y:
                                                                 mjkdT     Conacatenate each element of T
                                                               jb          Join on newlines
                                                                      b    Add a newline and implicitly print

Спасибо @ Pietu1998 за несколько советов


Это может быть самая длинная программа Pyth, которую я когда-либо видел
Business Cat

=ZhZравно =hZи FNравно V.
PurkkaKoodari

@TheBikingViking Что именно вы имеете в виду под достаточным временем? Я вполне уверен, что это не решило бы 25x25 к настоящему времени, если бы вы начали это с концепции вселенной.
Говрат

1
@Gowrath Я также довольно уверен в этом! Я новичок в Pyth, и после того, как это заняло у меня много времени, я даже не хочу рассматривать попытки реализовать лучший алгоритм
TheBikingViking

2

Javascript (ES6), 401 386 333 байта

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

Например, он переведет подсказку [3,1]в следующее регулярное выражение:

/^0*1{3}0+1{1}0*$/

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

Код

(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

Выход

Решение отображается в двоичном формате. Такие как:

00110
01110
11100
11101
00001

Тестовое задание

Это простой тест на примере сетки.

let f =
(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

console.log(f(
  [[2],[3],[4],[2],[2]],
  [[2],[3],[3],[3,1],[1]]
));


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

2

Haskell, 109 байт

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

import Data.List
n=mapM id
a#b=[x|x<-n$(n$" #"<$a)<$b,g x==a,g(transpose x)==b]
g=map$max[0].map length.words

Пример использования: [[2],[3],[3],[3,1],[1]] # [[2],[3],[4],[2],[2]]-> [[" ## "," ### ","### ","### #"," #"]].

Грубая сила. Попробуйте все комбинации и #, разделите int на части #, посчитайте длины и сравните с вводом.


1

PHP, 751 833 (720) 753 724 726 710 691 680 682 байта

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

$p=[];foreach($r as$y=>$h){for($d=[2-($n=count($h)+1)+$u=-array_sum($h)+$w=count($r)]+array_fill($i=0,$n,1),$d[$n-1]=0;$i<1;$d[0]+=$u-array_sum($d)){$o=$x=0;foreach($d as$i=>$v)for($x+=$v,$k=$h[$i];$k--;)$o+=1<<$x++;if(($s[$y]|$o)==$o){$p[$y][]=$o;$q[$y]++;}for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);if(++$i<$n)for($d[$i]++;$i--;)$d[$i]=1;}}
function s($i,$m){global$c,$w,$p;for(;!$k&&$i[$m]--;$k=$k&$m<$w-1?s($i,$m+1):$k){for($k=1,$x=$w;$k&&$x--;){$h=$c[$x];for($v=$n=$z=$y=0;$k&&$y<=$m;$y++)$n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];if($k&$v)$k=$n<=$h[$z];}}return$k?is_array($k)?$k:$i:0;}
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));
  • ожидает подсказки в массивах $rдля подсказок строк, $cдля подсказок столбцов и $sдля квадратных подсказок.
  • бросает, invalid argument supplied for foreachесли не находит решения.
  • чтобы получить правильное количество байтов, используйте физическое \nи удалите два других перевода строки.

описание

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

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

3) решение для печати


Последнее игра в гольф оказало серьезное влияние на производительность;
но я удалил задания профилирования для окончательных тестов.

Замените $n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];
на, if(($p[$y][$i[$y]]>>$x&1)-$v){$k=($v=!$v)||$n==$h[$z++];$n=1;}else$n++;
чтобы отменить последний шаг в гольф.

Примеры

Для небольшого примера (от 17 до 21 около 12 8 7 6,7 5,3 мс) используйте

$r=[[2],[3],[3],[3,1],[1]];$c=[[2],[3],[4],[2],[2]];$s=[0,0,0,0,0];

для рождественской головоломки:

  • убил мой маленький домашний сервер со старым решением
  • убил браузер с тестовыми выводами
  • теперь решается за 50 37,8 45,5 около 36 секунд

поместите данные из вопроса в файл christmas.nonogramи используйте этот код для импорта:

$t=r;foreach(file('christmas.nonogram')as$h)if('-'==$h=trim($h))$t=c;elseif('#'==$h){$t=s;$f=count($h).b;}else
{$v=explode(' ',$h);if(s==$t)for($h=$v,$v=0,$b=1;count($h);$b*=2)$v+=$b*array_shift($h);${$t}[]=$v;}

сломать

$p=[];  // must init $p to array or `$p[$y][]=$o;` will fail
foreach($r as$y=>$h)
{
    // walk $d through all combinations of $n=`hint count+1` numbers that sum up to $u=`width-hint sum`
    // (possible `0` hints for $h) - first and last number can be 0, all others are >0
    for(
        $d=[2-
            ($n=count($h)+1)+               // count(0 hint)=count(1 hint)+1
            $u=-array_sum($h)+$w=count($r)  // sum(0 hint) = width-sum(1 hint)
        ]                           // index 0 to max value $u-$n+2
        +array_fill($i=0,$n,1)      // other indexes to 1
        ,$d[$n-1]=0;                // last index to 0
                                    // --> first combination (little endian)
        $i<1;   // $i:0 before loop; -1 after increment; >=$n after the last combination
        $d[0]+=$u-array_sum($d) // (see below)
    )
    {
        // A: create row (binary value) from 1-hints $h and 0-hints $d
        $o=$x=0;
        foreach($d as$i=>$v)
            for($x+=$v,$k=$h[$i];$k--;)
                $o+=1<<$x++;
        // B: if $o satisfies the square hints
        if(($s[$y]|$o)==$o)
        {
            $p[$y][]=$o;    // add to possible combinations
            $q[$y]++;       // increase possibility counter
        }
        // C: increase $d
            // find lowest index with a value>min
                // this loop doesn´t need to go to the last index:
                // if all previous values are min, there is nothing left to increase
        for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);
        if(++$i<$n)             // index one up; increase $d if possible
            for($d[$i]++        // increase this value
            ;$i--;)$d[$i]=1;    // reset everything below to 1
            // adjust $d[0] to have the correct sum (loop post condition)
    }
}

// search solution: with backtracking on the row combinations ...
function s($i,$m)
{
    global $c,$w,$p;
    for(;
        !$k // solution not yet found
        &&$i[$m]    // if $i[$m]==0, the previous iteration was the last one on this row: no solution
            --;     // decrease possibility index for row $m
        $k=$k&$m<$w-1? s($i,$m+1) : $k      // if ok, seek deeper while last row not reached ($m<$w-1)
    )
    {
        // test if the field so far satisfies the column hints: loop $x through columns
        for($k=1,$x=$w;$k&&$x--;)   // ok while $k is true
        {
            $h=$c[$x];
            // test column hints on the current combination: loop $y through rows up to $m
            for($v=$n=$z=   // $v=temporary value, $n=temporary hint, $z=hint index
                $y=0;$k&&$y<=$m;$y++)
                // if value has not changed, increase $n. if not, reset $n to 1
                // (or 0 for $k=false; in that case $n is irrelevant)
                $n=$n*  
                    // $f=false (int 0) when value has changed, true (1) if not
                    ($f=($p[$y][$i[$y]]>>$x&1)==$v)
                    +$k=$f?:    // ok if value has NOT changed, else
                        ($v=!$v)        // invert value. ok if value was 0
                        || $n==$h[$z    // value was 1: ok if temp hint equals current sub-hint
                        ++]             // next sub-hint
                ;
            // if there is a possibly incomplete hint ($v==1)
            // the incomplete hint ($n) must be <= the next sub-hint ($c[x][$z])
            // if $n was <$h[$z] in the last row, the previous column hints would not have matched
            if($k&$v)$k=$n<=$h[$z];
        }
        // ok: seek deeper (loop post condition)
        // not ok: try next possibility (loop pre condition)
    }
    return$k?is_array($k)?$k:$i:0;  // return solution if solved, 0 if not
}

// print solution
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));

1
Большой пример убивает мой маленький домашний сервер (500 - Internal Server Error). Комбинации готовы через 15 секунд, но декартово произведение имеет 1.823E + 61 член. (Между 7-й и 22-й строкой есть только одно решение.) Алгоритм должен быть улучшен.
Тит

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

@gowrath: обратное отслеживание дает немного и даже сохраняет байты ... целое число с битовой арифметикой дает скорость примерно на 50%, но увеличивает размер (нужно еще выяснить, сколько именно это стоит) ... Я все еще на нем.
Тит

@gowrath: я преследовал свою ошибку; это было в приращении (где еще?): $dдолжно быть в правильном порядке дляforeach
Титу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.