ブリティッシュインテリジェンスを倒せますか?(ノノグラムソルバー)


20

ブリティッシュインテリジェンスを打ち負かすという危険な探求に乗り出す時が来ました。この課題の目的は、ノノグラムを解決する最短のコードを書くことです。

ノノグラムとは何ですか?

ノノグラムパズル

ルールは簡単です。正方形のグリッドがあり、黒で塗りつぶすか、空白のままにする必要があります。グリッドの各行の横には、その行の黒い四角の実行の長さがリストされます。各列の上には、その列の黒い四角の連続の長さがリストされています。あなたの目的は、すべての黒い正方形を見つけることです。このパズルタイプでは、数字は、特定の行または列にある塗りつぶされた正方形の切れ目のないラインの数を測定する離散トモグラフィーの形式です。たとえば、「4 8 3」の手がかりは、4、8、および3つの塗りつぶされた正方形のセットがこの順序であり、連続するグループ間に少なくとも1つの空白の正方形があることを意味します。[ 1 ] [ 2 ]

したがって、上記のノノグラムの解決策は次のとおりです。

解決されたノノグラム

実装の詳細

任意の方法でNonogramを表現す​​ることを選択し、言語に適した方法で入力として使用することができます。出力についても同様です。この課題の目的は、文字通り仕事を成し遂げることです。プログラムが提供する出力で非グラムを解くことができれば、それは有効です。1つの注意点は、オンラインソルバーを使用できないことです:)

この問題は完全に効率的な解決策がないという点で非常にアルゴリズム的に挑戦的(np-complete)であり、そのため、大きな問題を解決できないことに対するペナルティはありませんが、そうであれば答えは大きく報われます大きなケースを処理できる(ボーナスを参照)。ベンチマークとして、私のソリューションは5〜10秒以内に最大約25x25で動作します。さまざまな言語間で柔軟性を持たせるには、25x25のノノグラムで5分未満のソリューションで十分です。

常に正方形の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の可能な構成があります。コンテキストでは、それは既知の宇宙の原子数の2倍以上です(つまり、宇宙の各原子を少し使用した場合、可能性を格納するのに十分なスペースがまだありません)。時間の面で、各構成の妥当性を確認するのにナノ秒(寛大な)がかかった場合、コードの実行が完了するには宇宙の7つのライフタイムがかかります:)
gowrath

1
解決不可能なケースを明確にするためのタイ。(+私は魔法のPCを持っており、約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

Brachylog70 69バイト

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

これは、2つのリストのリスト(最初に行インジケーター、次に列インジケーター)を取ります。各インジケーターはそれ自体がリストです([3,1]1行のような状況の場合)。

このバージョンでは、5 x 5のチャレンジの例を解決するのに約3分かかります。

より効率的なバージョン、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の「ブロック」が指示として与えられたものと一致する制約を持つ1つのグリッドを見つけることです。

これは、チャレンジの5 x 5の例では約0.05秒かかります。制約の観点から1つ以上のゼロで区切られたブロックをどのように表現するかわからないので、これはボーナスの場合にはまだ遅すぎます。

説明

93バイトバージョンについて説明します。2つの唯一の違いは、70バイトバージョンには存在しない述語3の呼び出しと、述語の番号付けです(1つ少ないため)。

  • 主な述語:

    [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:1のブロックがインジケーターと一致することを確認します

    @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のブロックの場合はtrue、それ以外の場合はfalse

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

仕事に最適なツールのように感じます。説明を楽しみにしています。
エミグナ

@Fatalizeこれは素晴らしいです、私は誰かがこれを行うためにプロローグ風の言語を使用するのを待っていました。25x25のケースで試しましたか?既に
-gowrath

@gowrath今日の午後、これをコンピューターで実行します。何が起こるか見てみましょう。
16

@Fatalizeタイムアウトのようですが、間違っている可能性があります。データ入力スキルに完全に依存することもありません:D
gowrath

@gowrath TIOではタイムアウトしますが、コンピューターのオフラインインタープリターで直接実行します。
16

9

Haskell、242230201199177163160149131バイト

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))。簡単なベンチマークでは、5x5の非グラムに86GBが割り当てられていることが明らかになりました。

おもしろい事実:正方形のものだけでなく、すべての非グラムに対して機能します。


使い方:

  • a#b:正方形の数を表す整数のリストのリストが与えられた場合、すべてのグリッド(map(chunk$length b).mapM id$a>>b>>[[0,1]])を生成し、結果をフィルター処理して有効なグリッドのみを保持します。
  • g:潜在的なノノグラムが与えられると、1の実行を水平方向に合計します。

O((len a * len b)^ 2)ではなく、O(2 ^(len a * len b))を意味します。
アンデルスカセオルグ16

@AndersKaseorgそうです。私が誤って暗示した百万を保管してください。:D
ThreeFx

1
別の数バイト:m(chunk$l b)およびreplicate(l$a>>b)
-Bergi

@ThreeFx 86GB:O ...ところで、これをコンパイルする方法を簡単に説明できますか?私はhaskellの学習を始めたばかりで、これによりghcでエラーが発生しています。それを試してみたい:)
gowrath

1
import Data.Listsとの両方Data.Listを再エクスポートするため、十分Data.List.Splitです。
nimi

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

強引な。すべての組み合わせを試してみてください#、のスプリット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を使用し、他の2つの改行を削除します。

説明

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++;
、最後のゴルフのステップを元に戻します。

小さな例(12 8 7 6.7 5.3ミリ秒前後の17から21)では、

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

クリスマスパズルの場合:

  • 古いソリューションで私の小さなホームサーバーを殺した
  • テスト出力でブラウザを強制終了しました
  • 36秒で50 37.8 45.5で解決

質問からデータをファイルに入れ、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個のseconsの後に組み合わせが用意されていますが、デカルト積には1.823E + 61メンバーが含まれています。(7番目と22番目の行には1つのソリューションしかありません。)アルゴリズムを改善する必要があります。
タイタス

再帰的なバックトラッキングを使用すると、これは高速化されると思います。それにもかかわらず、素晴らしい仕事です!
-gowrath

@gowrath:バックトラッキングはビットを提供し、バイトを節約します...ビット演算の整数は約50%の速度を提供しますが、サイズは増加します(正確にどれだけのコストが必要かをまだ見つけなければなりません)...私はまだそれに取り組んでいます。
タイタス

@gowrath:バグを追いかけました。それは増分でした(他のどこ?):$d正しい順序である必要がありますforeach
タイタス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.