Nonographic Magnitude Optimizer ™ 구축


12

노노 그램은 다음과 같이 연속 영역 목록에 따라 흑백 사진을 그리는 것이 목표 인 일본 퍼즐 게임입니다.

"람다"가 포함 된 예제 비노 그램입니다.

행 또는 열의 비 그래픽 크기 를 해당 행 또는 열의 연속 된 검은 색 영역의 수로 정의하십시오 . 예를 들어, 맨 위 행의 비 그래픽 크기는 1입니다. 해당 행에 2 개의 사각형의 한 영역이 있기 때문입니다. 8 행에는 2, 2, 1이 있기 때문에 비 그래픽 크기는 3입니다.

빈 행이나 열의 비 인쇄 크기는 0입니다.


당신의 임무는 nonogram에 대한 솔루션 그리드를 취하고 모든 행과 열이 주어진 솔루션 그리드와 동일한 nonographic magnutide를 갖는 경우 가능한 적은 채워진 사각형으로 솔루션 그리드를 생성하는 프로그램을 작성 하는 것입니다.

예를 들어, 모든 정사각형이 채워진 비 모노그램 그리드는 모든 행 또는 열에서 1의 비 그래픽 크기가 1입니다.

모든 정사각형이 채워지는 10x10 노노 그램.

그리드를 통해 대각선 줄무늬를 가짐으로써 채워진 사각형의 수를 크게 줄임으로써 동일한 비 그래픽 크기를 얻을 수 있습니다.

위와 동일한 비 그래픽 크기의 10x10 비노 그램.


프로그램은 이 파일 에서 50,000 줄로 구성된 입력 (1.32MB tar.gz 텍스트 파일; 2.15MB 압축 해제) 을받습니다 . 각각은 무작위로 (80 % 검은 색) 사각형으로 채워진 단일 16 × 16 노노 그램 솔루션 그리드를 나타냅니다. 각각 해당 입력 그리드에 대해 최적화 된 솔루션 그리드를 포함하는 다른 50,000 라인을 출력합니다.

각 그리드는 43 자 (왼쪽에서 오른쪽으로, 다음에서 위에서 아래로 정사각형 인코딩)가 포함 된 base64 문자열로 표시되며 프로그램은 동일한 형식으로 출력을 리턴해야합니다. 예를 들어, 파일의 첫 번째 그리드는입니다 E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=.

첫 번째 예 비노 그램

그리드는로 E매핑되는 것으로 시작 000100하므로 맨 위 행의 처음 6 개 셀은 네 번째 셀을 제외한 모두 흰색입니다. 다음 문자는 /에 매핑되는 문자 111111이므로 다음 6 개의 셀은 모두 검은 색입니다.


프로그램은 실제로 50,000 개의 테스트 사례 각각에 대해 정확한 비 그래픽 크기로 솔루션 그리드를 반환해야합니다. 더 나은 것을 찾지 못하면 입력과 동일한 그리드를 반환 할 수 있습니다.

가장 적은 총 채워진 사각형 (모든 언어로)을 반환하는 프로그램이 승자이며 짧은 코드는 순위 결정입니다.


현재 점수 판 :

  1. 3,637,260 — Sleafar, 자바
  2. 7,270,894 — Flawr, Matlab
  3. 10,239,288 — 조 Z., 배쉬

1
나는 기본 64 인코딩의 요점을 실제로 보지 못하고 그와 함께 작업하는 것이 진짜 고통입니다. 1과 0의 라인을 만드는 것이 더 쉽지 않습니까? 아니면 모든 것을 비트 맵으로 인코딩합니까?
flawr

@flawr : 주로 파일 크기를 줄입니다 (주로 1과 0에 비해 6 배). 또한 비트 맵은 작업하기가 더 어려울 것입니다.
Joe Z.

흑백 이미지, 읽기 / 쓰기가 쉽고 b64 인코딩과 같은 크기로 만들 수 있습니다.
flawr

2
또한 입력 및 / 또는 출력을위한 b64 인코딩의 팬이 아닙니다. 왜 I / O를 편리한 형식으로 두지 않겠습니까?
Sparr

1
그것이 있다고 가정하면, 내일이 시간까지 최적의 솔루션이 있어야합니다.
quintopia

답변:


7

Python 2 & PuLP — 2,644,688 제곱 (최적화 최소화); 10,753,553 제곱 (최적화 됨)

최소 1152 바이트로 골프

from pulp import*
x=0
f=open("c","r")
g=open("s","w")
for k,m in enumerate(f):
 if k%2:
    b=map(int,m.split())
    p=LpProblem("Nn",LpMinimize)
    q=map(str,range(18))
    ir=q[1:18]
    e=LpVariable.dicts("c",(q,q),0,1,LpInteger)
    rs=LpVariable.dicts("rs",(ir,ir),0,1,LpInteger)
    cs=LpVariable.dicts("cs",(ir,ir),0,1,LpInteger)
    p+=sum(e[r][c] for r in q for c in q),""
    for i in q:p+=e["0"][i]==0,"";p+=e[i]["0"]==0,"";p+=e["17"][i]==0,"";p+=e[i]["17"]==0,""
    for o in range(289):i=o/17+1;j=o%17+1;si=str(i);sj=str(j);l=e[si][str(j-1)];ls=rs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,"";l=e[str(i-1)][sj];ls=cs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,""
    for r,z in enumerate(a):p+=lpSum([rs[str(r+1)][c] for c in ir])==2*z,""
    for c,z in enumerate(b):p+=lpSum([cs[r][str(c+1)] for r in ir])==2*z,""
    p.solve()
    for r in ir:
     for c in ir:g.write(str(int(e[r][c].value()))+" ")
     g.write('\n')
    g.write('%d:%d\n\n'%(-~k/2,value(p.objective)))
    x+=value(p.objective)
 else:a=map(int,m.split())
print x

(NB : 많이 들여 쓴 줄은 공백이 아닌 탭으로 시작합니다.)

출력 예 : https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing

같은 문제는 Integer Linear Programs로 쉽게 변환 할 수 있으며, 내 자신의 프로젝트에 PuLP (다양한 LP 솔버를위한 파이썬 인터페이스)를 사용하는 방법을 배우는 데 기본적인 문제가 필요했습니다. 또한 PuLP는 사용하기가 매우 쉽고, ungolfed LP 빌더는 처음 시도했을 때 완벽하게 작동했습니다.

분기 및 바운드 IP 솔버를 사용하여 나를 위해이 문제를 해결하기 위해 열심히 노력하는 두 가지 좋은 점은 (분기 및 바운드 솔버를 구현하지 않아도 됨)

  • 특수 목적 솔버는 정말 빠릅니다. 이 프로그램은 상대적으로 저렴한 가정용 PC에서 약 17 시간 만에 50000 개의 모든 문제를 해결합니다. 각 인스턴스를 해결하는 데 1-1.5 초가 걸렸습니다.
  • 그들은 최적의 솔루션을 보장합니다 (또는 그들이 실패했다고 말합니다). 따라서 아무도 제 점수를 제곱으로 이길 수 없다고 확신 할 수 있습니다.

이 프로그램을 사용하는 방법

먼저 PuLP를 설치해야합니다. pip install pulppip가 설치되어 있으면 트릭을 수행해야합니다.

그런 다음 "c"라는 파일에 다음을 입력해야합니다. https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing

그런 다음 동일한 디렉토리에서 최신 Python 2 빌드로이 프로그램을 실행하십시오. 하루도 채 안되어서, "s"라는 파일이 있으며, 여기에는 각각 아래에 나열된 총 채워진 사각형 수가있는 50,000 개의 해결 된 노노 그램 그리드 (판독 가능한 형식)가 들어 있습니다.

채워진 사각형의 수를 최대화하려면 대신 LpMinimize8 행을 대신으로 변경하십시오 LpMaximize. https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharing 과 같은 출력이 표시됩니다.

입력 형식

이 프로그램은 수정 된 입력 형식을 사용합니다. Joe Z.는 OP에 대한 의견이 있으면 입력 형식을 다시 인코딩 할 수 있다고 말했습니다. 모양을 보려면 위의 링크를 클릭하십시오. 각각 16 개의 숫자를 포함하는 10000 개의 줄로 구성됩니다. 짝수 번째 줄은 주어진 인스턴스의 행에 대한 크기이고 홀수 번째 줄은 위의 행과 같은 인스턴스의 열에 대한 크기입니다. 이 파일은 다음 프로그램에 의해 생성되었습니다.

from bitqueue import *

with open("nonograms_b64.txt","r") as f:
    with open("nonogram_clues.txt","w") as g:
        for line in f:
            q = BitQueue(line.decode('base64'))
            nonogram = []
            for i in range(256):
                if not i%16: row = []
                row.append(q.nextBit())
                if not -~i%16: nonogram.append(row)
            s=""
            for row in nonogram:
                blocks=0                         #magnitude counter
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s
            nonogram = map(list, zip(*nonogram)) #transpose the array to make columns rows
            s=""
            for row in nonogram:
                blocks=0
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s

(이 재 인코딩 프로그램은 또한 위에서 언급 한 동일한 프로젝트를 위해 만든 사용자 정의 BitQueue 클래스를 테스트 할 수있는 추가 기회를 제공했습니다. 단순히 비트 OR 바이트 시퀀스로 데이터를 푸시 할 수있는 큐입니다. 한 번에 비트 또는 바이트로 표시됩니다 (이 경우 완벽하게 작동 함).

ILP를 작성하는 특정 이유로 입력을 다시 인코딩했습니다. 크기를 생성하는 데 사용 된 그리드에 대한 추가 정보는 완벽하게 쓸모가 없습니다. 크기는 유일한 제약이므로 크기에 액세스해야합니다.

Ungolfed ILP 빌더

from pulp import *
total = 0
with open("nonogram_clues.txt","r") as f:
    with open("solutions.txt","w") as g:
        for k,line in enumerate(f):
            if k%2:
                colclues=map(int,line.split())
                prob = LpProblem("Nonogram",LpMinimize)
                seq = map(str,range(18))
                rows = seq
                cols = seq
                irows = seq[1:18]
                icols = seq[1:18]
                cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
                rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
                colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)
                prob += sum(cells[r][c] for r in rows for c in cols),""
                for i in rows:
                    prob += cells["0"][i] == 0,""
                    prob += cells[i]["0"] == 0,""
                    prob += cells["17"][i] == 0,""
                    prob += cells[i]["17"] == 0,""
                for i in range(1,18):
                    for j in range(1,18):
                        si = str(i); sj = str(j)
                        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                for r,clue in enumerate(rowclues):
                    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
                for c,clue in enumerate(colclues):
                    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""
                prob.solve()
                print "Status for problem %d: "%(-~k/2),LpStatus[prob.status]
                for r in rows[1:18]:
                    for c in cols[1:18]:
                        g.write(str(int(cells[r][c].value()))+" ")
                    g.write('\n')
                g.write('Filled squares for %d: %d\n\n'%(-~k/2,value(prob.objective)))
                total += value(prob.objective)
            else:
                rowclues=map(int,line.split())
print "Total number of filled squares: %d"%total

이것은 실제로 링크 된 "예제 출력"을 생성 한 프로그램입니다. 따라서 각 그리드의 끝에 여분의 긴 줄이 있는데, 골프를 칠 때 잘 렸습니다. (골프 버전은 단어를 빼고 동일한 출력을 생성해야합니다 "Filled squares for ")

작동 원리

cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)

나는 중심 16x16 부분이 실제 퍼즐 솔루션 인 18x18 그리드를 사용합니다. cells이 그리드입니다. 첫 번째 줄은 324 개의 이진 변수 "cell_0_0", "cell_0_1"등을 만듭니다. 또한 그리드의 솔루션 부분에서 셀 사이와 주변의 "공간"그리드를 만듭니다. rowseps셀을 가로로 구분하는 공백을 상징하는 289 개의 변수를 colseps가리키고 , 셀을 세로로 구분하는 공백을 표시하는 변수를 가리킨다. 유니 코드 다이어그램은 다음과 같습니다.

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0S와 S는 추적 이진 값인 cell변수 상기 |의 이진 값에 의해 추적 rowsep변수와 -의 이진 값에 의해 추적 colsep변수.

prob += sum(cells[r][c] for r in rows for c in cols),""

이것은 목적 함수입니다. 모든 cell변수 의 합입니다 . 이들은 이진 변수이므로 솔루션에서 채워진 제곱의 수입니다.

for i in rows:
    prob += cells["0"][i] == 0,""
    prob += cells[i]["0"] == 0,""
    prob += cells["17"][i] == 0,""
    prob += cells[i]["17"] == 0,""

이것은 그리드의 바깥 가장자리 주위의 셀을 0으로 설정합니다 (위의 0으로 표시 한 이유입니다). 이것은 채워지지 않은 셀에서 채워진 셀로 채워진 (열 또는 행을 가로 질러 이동하는) 모든 변경 사항이 채워진 것에서 채워지지 않은 것까지 (그리고 그 반대로) 일치하도록 보장하기 때문에 채워진 셀의 "블록"수를 추적하는 가장 편리한 방법입니다. ), 행의 첫 번째 또는 마지막 셀이 채워져도 마찬가지입니다. 이것이 18x18 그리드를 사용하는 유일한 이유입니다. 블록을 계산하는 유일한 방법은 아니지만 가장 간단하다고 생각합니다.

for i in range(1,18):
    for j in range(1,18):
        si = str(i); sj = str(j)
        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""
        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""

이것이 ILP 논리의 진정한 고기입니다. 기본적으로 각 셀 (첫 번째 행과 열의 셀 제외)은 셀의 논리 xor이고 행의 왼쪽과 열의 바로 위에있는 구분 기호 여야합니다. 이 훌륭한 답변에서 {0,1} 정수 프로그램 내에서 xor를 시뮬레이트하는 제약 조건을 얻었습니다. /cs//a/12118/44289

좀 더 설명하기 위해 :이 xor 제약 조건은 구분 기호가 0과 1 인 셀 사이에있는 경우에만 구분 기호가 1이되도록합니다 (채움이 채워지지 않은 상태에서 채워진 상태로 또는 그 반대로 변경 표시). 따라서 행 또는 열에 해당 행 또는 열의 블록 수보다 정확히 두 배 많은 1 값 구분 기호가 있습니다. 다시 말해, 주어진 행 또는 열의 구분 기호의 합은 해당 행 / 열의 크기의 정확히 두 배입니다. 따라서 다음과 같은 제약 조건이 있습니다.

for r,clue in enumerate(rowclues):
    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
for c,clue in enumerate(colclues):
    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""

그리고 그것은 거의 다입니다. 나머지는 기본 솔버에게 ILP를 풀도록 요청한 다음 결과 솔루션을 파일에 쓸 때 형식을 지정합니다.


정말 좋은 대답입니다. LP 솔버에 대해 배우고 싶습니다. 당신은 19x19, 6 색 보드 (솔루션 계산 시간과 관련하여 )에 대한 홍수 그것 퍼즐 (링크) 을 해결하는 데 사용될 수 있다고 생각 합니까? 나는 그 콘테스트에 이미 답변을 받았고 (내가 얻은) 내 방법 (A * 검색 알고리즘)은 최적이 아닌 솔루션 만 제공합니다.
tigrou

@tigrou 감사합니다. 홍수 문제가 그러한 해결책을 인정할 정도로 선형인지 확실하지 않습니다. 이런 식으로 모델링하는 방법을 확실히 알 수 없습니다.
quintopia

누군가 이미 그것을 시도한 것 같습니다 : kunigami.blog/2012/09/16/flood-it-an-exact-approach 그러나 그들은 14x14 보드에 대한 적절한 시간 내에 최적의 솔루션을 얻을 수 없었습니다.
tigrou

3

Java, 6,093,092 4,332,656 3,637,260 제곱 (최소화), 10,567,550 10,567,691 10,568,746 제곱 (최대화)

프로그램의 두 변형은 모두 크기를 변경하지 않고 소스 그리드에서 반복적으로 작업을 수행합니다.

최소화 기

수축()

여기에 이미지 설명을 입력하십시오

검은 색 사각형에 90 ° 각도로 2 개의 흰색 이웃과 2 개의 검은 색 이웃이 있으면 흰색 사각형으로 대체 할 수 있습니다.

moveLine ()

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

위와 같은 구성에서 검은 선을 오른쪽으로 옮길 수 있습니다. 이것은 새로운 수축 가능성을 열기 위해 시계 방향과 시계 반대 방향으로 모든 4 라인 방향에 대해 반복적으로 수행됩니다.

최대 화기

main()이 버전에 대해 줄을 주석 해제하고 위 의 줄을 주석 처리하십시오 .

grow ()

여기에 이미지 설명을 입력하십시오

흰색 사각형에 90 ° 각도로 2 개의 흰색 이웃과 2 개의 검은 이웃이 있으면 검은 사각형으로 대체 할 수 있습니다.

moveLine ()

Minimizer와 동일합니다.

출처

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Function;

public class Main {
    private static final int SIZE = 16;
    private static final int SIZE_4 = SIZE + 4;
    private static final int E = 0;
    private static final int N = 1;
    private static final int W = 2;
    private static final int S = 3;

    private static final Base64.Decoder decoder = Base64.getMimeDecoder();
    private static final Base64.Encoder encoder = Base64.getMimeEncoder();
    private static int sourceBlack = 0;
    private static int targetBlack = 0;

    private static class Nonogram {
        private final boolean[] cells = new boolean[SIZE_4 * SIZE_4];
        private final int[] magnitudes;

        public Nonogram(String encoded) {
            super();
            byte[] decoded = decoder.decode(encoded);
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    if ((decoded[i] & (1 << (7 - j))) != 0) {
                        int k = i * 8 + j;
                        cells[getPos(k / SIZE, k % SIZE)] = true;
                        ++ sourceBlack;
                    }
                }
            }
            magnitudes = calcMagnitudes();
        }

        private int getPos(int row, int col) {
            return (row + 2) * SIZE_4 + col + 2;
        }

        private int move(int pos, int dir, int count) {
            switch (dir) {
                case E: return pos + count;
                case N: return pos - count * SIZE_4;
                case W: return pos - count;
                case S: return pos + count * SIZE_4;
                default: return pos;
            }
        }

        private int move(int pos, int dir) {
            return move(pos, dir, 1);
        }

        private int[] calcMagnitudes() {
            int[] result = new int[SIZE * 2];
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    int pos = getPos(row, col);
                    if (cells[pos]) {
                        if (!cells[move(pos, W)]) {
                            ++ result[row + SIZE];
                        }
                        if (!cells[move(pos, N)]) {
                            ++ result[col];
                        }
                    }
                }
            }
            return result;
        }

        private boolean isBlack(int pos) {
            return cells[pos];
        }

        private boolean isWhite(int pos) {
            return !cells[pos];
        }

        private boolean allBlack(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isWhite(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private boolean allWhite(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isBlack(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private int findWhite(int pos, int dir) {
            int count = 0;
            int p = pos;
            while (cells[p]) {
                ++ count;
                p = move(p, dir);
            }
            return count;
        }

        @SafeVarargs
        private final void forEach(Function<Integer, Boolean>... processors) {
            outer:
            for (;;) {
                for (Function<Integer, Boolean> processor : processors) {
                    for (int row = 0; row < SIZE; ++ row) {
                        for (int col = 0; col < SIZE; ++ col) {
                            if (processor.apply(getPos(row, col))) {
                                continue outer;
                            }
                        }
                    }
                }
                return;
            }
        }

        private boolean shrink(int pos) {
            if (cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = false;
                return true;
            }
            return false;
        }

        private boolean grow(int pos) {
            if (!cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = true;
                return true;
            }
            return false;
        }

        private boolean moveLine(boolean clockwise, int dir, int sourcePos) {
            int from = (dir + (clockwise ? 1 : 3)) % 4;
            int to = (dir + (clockwise ? 3 : 1)) % 4;
            int opp = (dir + 2) % 4;
            if (isBlack(sourcePos) && isWhite(move(sourcePos, from)) && isWhite(move(sourcePos, dir))) {
                int toCount = findWhite(move(move(sourcePos, dir), to), to) + 1;
                if (allWhite(move(sourcePos, to), to, toCount + 1)) {
                    int lineCount = 1;
                    int tmpPos = move(sourcePos, opp);
                    while (isBlack(tmpPos) && isWhite(move(tmpPos, from)) && allWhite(move(tmpPos, to),  to, toCount + 1)) {
                        ++ lineCount;
                        tmpPos = move(tmpPos, opp);
                    }
                    if (allBlack(tmpPos, to, toCount + 1)) {
                        tmpPos = sourcePos;
                        for (int j = 0; j < lineCount; ++ j) {
                            cells[tmpPos] = false;
                            cells[move(tmpPos, to, toCount)] = true;
                            tmpPos = move(tmpPos, opp);
                        }
                        return true;
                    }
                }
            }
            return false;
        }

        public Nonogram minimize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> shrink(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> shrink(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public Nonogram maximize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> grow(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> grow(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public String toBase64() {
            if (!Arrays.equals(magnitudes, calcMagnitudes())) {
                throw new RuntimeException("Something went wrong!");
            }
            byte[] decoded = new byte[SIZE * SIZE / 8];
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    int k = i * 8 + j;
                    if (cells[getPos(k / SIZE, k % SIZE)]) {
                        decoded[i] |= 1 << (7 - j);
                        ++ targetBlack;
                    }
                }
            }
            return encoder.encodeToString(decoded);
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    b.append(cells[getPos(row, col)] ? '#' : ' ');
                }
                b.append('\n');
            }
            return b.toString();
        }
    }

    public static void main(String[] args) throws Exception {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("solutions_b64.txt"));
                BufferedReader reader = new BufferedReader(new FileReader("nonograms_b64.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(new Nonogram(line).minimize().toBase64() + "\n");
                //writer.write(new Nonogram(line).maximize().toBase64() + "\n");
            }
        }
        System.out.printf("%d -> %d", sourceBlack, targetBlack);
    }
}

1

배쉬-10,239,288 평방

마지막 참조 솔루션으로 :

cp nonograms_b64.txt solutions_b64.txt

더 나은 솔루션을 찾지 못하면 프로그램에서 동일한 그리드를 반환 할 수 있으므로 전체 파일을 그대로 인쇄하는 것도 유효합니다.

테스트 파일에는 총 10,239,288 개의 검은 사각형이 있으며, 이는 256 개의 사각형을 가진 50,000 개의 그리드 중 80 %로 채워진 사각형의 80 %에서 예상되는 10,240,000에 거의 가깝습니다. 테스트 배터리 질문과 마찬가지로, 이번에는 점수가 4 백만 또는 5 백만에 가까울 것으로 예상되지만 최적의 점수가 2 백만 범위에이를 것으로 예상하는 테스트 사례 수를 선택했습니다. .


누구나 검은 사각형을 최소화하는 대신 최대화 하고 10,240,000 이상을 얻는 솔루션을 만들 수 있다면 현상금을주는 것을 고려할 수 있습니다.


1

Matlab, 7,270,894 제곱 (원본의 ~ 71 %)

아이디어는 단순하고 반복적 인 탐욕스러운 검색입니다. 모든 검은 사각형에 대해 비 그래픽 크기를 변경하지 않고 흰색으로 설정할 수 있는지 확인하십시오. 이것을 두 번 반복하십시오. (더 많은 반복으로 더 나은 결과를 얻을 수는 있지만 무료는 아닙니다. 결과적으로 해당 런타임이 길어집니다. 이제 약 80 분이 걸렸습니다. 50k 테스트 사례를 모두 계산할 필요가 없다면 그렇게 할 것입니다 ...)

여기에 코드 (평소와 같이 개별 파일의 각 기능)

function D = b64decode(E)
% accepts a string of base 64 encoded data, and returns a array of zeros
% and ones
F = E;
assert( mod(numel(E),4)==0 && 0 <= sum(E=='=') && sum(E=='=') <= 2,'flawed base 64 code')

F('A' <= E & E<= 'Z') = F('A' <= E & E<= 'Z') -'A';       %upper case
F('a' <= E & E<= 'z') = F('a' <= E & E<= 'z') -'a' + 26;  %lower case
F('0'<= E & E <= '9') = F('0'<= E & E <= '9') -'0' + 52;  %digits
F( E == '+') = 62;
F( E == '/') = 63;
F( E == '=') = 0;

D=zeros(1,numel(E)*3*8/4);

for k=1:numel(E);
    D(6*(k-1)+1 + (0:5)) = dec2bin(F(k),6)-'0';
end

if E(end) == '=';
    D(end-7:end) = [];
    if E(end-1) == '=';
        D(end-7:end) = [];
    end
end
end

function E = b64encode(D)
assert(mod(numel(D),8)==0,'flawed byte code');
N=0;
while mod(numel(D),6) ~= 0;
    D = [D,zeros(1,8)];
    N = N+1;
end
dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

E=zeros(1,numel(D)/6)+'=';
for k=0:numel(E)-N-1;
    E(k+1) = dict(bin2dec( [D(6*k+(1:6))+'0',''] ) + 1);
end

E = [E,''];
end


function [B,T,O] = reduce_greedy(N)
% reduce greedily
NM = nomographic_magnitude(N);
B = N; %current best
M = N;
T = nnz(N); %current number of filled squares
O = T;

for r=1:2;  %how many repetitions
    I = find(B);
    for k=1:numel(I);
        M = B;
        M( I(k) ) = 0;
        %check whether we have a valid solution
        if all(NM == nomographic_magnitude(M))
            if T > nnz(M); %did we actually reduce the number of filled squares?
                B = M;
                T = nnz(M);
            end
        end
    end
end


%% main file
string_in = fileread('nonograms_b64.txt');
string_out = string_in;
tic
total_new = 0;  %total number of black squares
total_old = 0;
M = 50000;
for k=1:M;
    disp(k/M); %display the progress
    line = string_in(45*(k-1)+(1:44));
    decoded = b64decode(line);        
    nonogram = reshape(decoded,16,[]) ;%store nonogram as a matrix
    [B,T,O] = reduce_greedy(nonogram);
    disp([nnz(B),nnz(nonogram)])
    total_new = total_new + T;
    total_old = total_old + O;
    string_in(45*(k-1)+(1:44)) = b64encode(B(:).');
end
toc
F = fopen('nonograms_b64_out.txt','w');
fprintf(F,string_out);
%%
disp([total_new,total_old])
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.