Чи можете ви побити британську розвідку? (Nonogram Solver)


20

Настав час розпочати небезпечні пошуки, щоб перемогти британську розвідку. Мета цього завдання - написати найкоротший код, який вирішить нонограму.

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

Нонограма головоломки

Правила прості. У вас є сітка квадратів, яку потрібно заповнити чорним кольором або залишити порожнім. Поруч із кожним рядком сітки вказані довжини прогонів чорних квадратів на цьому рядку. Над кожним стовпцем перераховані довжини прогонів чорних квадратів у цьому стовпці. Ваша мета - знайти всі чорні квадрати. У цьому типі головоломки числа є формою дискретної томографії, яка вимірює кількість нерозривних ліній заповнених квадратів у будь-якому заданому рядку чи стовпчику. Наприклад, підказка "4 8 3" означатиме, що в такому порядку є набори з чотирьох, восьми та трьох заповнених квадратів, принаймні один порожній квадрат між послідовними групами. [ 1 ] [ 2 ]

Тож рішенням вищевказаної нонограми було б:

Вирішено нонограму

Деталі впровадження

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

Ця проблема є дуже алгоритмічно складною (np-завершеною) тим, що не існує повністю ефективного рішення для неї, і як така, ви не будете штрафовані за те, що не зможете вирішити більші, хоча ваша відповідь буде сильно винагороджена, якщо вона буде вміє обробляти великі справи (див. бонус). Як орієнтир, моє рішення працює приблизно до 25x25 протягом 5-10 секунд. Щоб забезпечити гнучкість серед різних мов, рішення, які потребують менше 5 хвилин для нонограми 25x25, є досить хорошими.

Ви можете вважати головоломку завжди квадратною нонограмою NxN.

Ви можете використовувати цей онлайн-виробник головоломок для тестування рішень.

Оцінка балів

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

Бонус

Я на насправді дізнався про кросворди від криптографічного Різдвяної листівки випущеної британської розвідки тут . Перша частина була в основному масивною нонограмою розміром 25x25. Якщо ваше рішення зможе вирішити це, ви отримаєте кудо :)

Щоб полегшити ваше життя з точки зору введення даних, я запропонував, як я представляв дані для цієї конкретної головоломки для вашого безкоштовного використання. Перші 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]])

На жаль, мій веб-сайт не працює, але раніше він мав досить швидке вирішення нонограм; 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

Брахілог , 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,

Спробуйте в Інтернеті!

Це не є повною грубою силою: різниця полягає лише в тому, що ця накладає обмеження на значення комірок, таким чином, щоб число 1s у кожному рядку та стовпці відповідало цифрам, вказаним у якості вхідних даних. Єдина частина грубої сили - це пошук однієї сітки з тими обмеженнями, для яких "блоки" з 1s відповідають тому, що подано як вказівка.

На цей приклад 5 на 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: сума 1s у списку повинна бути дорівнює сумі показників (наприклад, якщо показник [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: Перевірте, чи блоки 1s відповідають показнику

    @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: Вірно для блоків 1s, інакше false

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

Відчуває себе ідеальним інструментом для роботи. З нетерпінням чекаю пояснення.
Емінья

@Fatalize Це фантастично, я чекав, коли хтось скористається мовою, що дозволяє пролог, щоб це зробити. Ви пробували це з корпусом 25x25? Я ввів дані для вас уже
gowrath

@gowrath Я запустив це на своєму комп’ютері сьогодні вдень, побачимо, що станеться.
Фаталізувати

@Fatalize Здається, що очікується час, але я можу робити це неправильно. Я також не повністю покладався на свої навички введення даних: D
gowrath

@gowrath Час у TIO закінчується, але я запускатиму його в режимі офлайн-перекладача безпосередньо на своєму комп’ютері.
Фаталізувати

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 Гб, виділених на 5х5 нонограми.

Факт забави: Він працює для всіх нонограм, не тільки квадратних.


Як це працює:

  • 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).
Anders Kaseorg

@AndersKaseorg Правильно. Тримайте мільйон, який я випадково мав на увазі. : D
ThreeFx

1
Ще кілька байтів: m(chunk$l b)іreplicate(l$a>>b)
Бергі

@ThreeFx 86GB: О ... Btw, ви можете коротко пояснити, як це скомпілювати? Я тільки почав вивчати 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 до цього часу, якби ви почали його із зачаття Всесвіту.
gowrath

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]]-> [[" ## "," ### ","### ","### #"," #"]].

Груба сила. Спробуйте всі комбінації та #, розділіть цілі шматки #, порахуйте довжини та порівняйте із вхідними.


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 - внутрішня помилка сервера). Комбінації готові після 15 секонів, але декартовий продукт налічує 1,823E + 61 член. (У 7-му та 22-му рядах є лише одне рішення btw.) Алгоритм повинен бути вдосконалений.
Тіт

Я думаю, що це може бути прискорено, якщо ви використовували рекурсивний зворотний трек. Тим не менше, чудова робота!
gowrath

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

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