สร้างเครื่องมือเพิ่มประสิทธิภาพ Nonographic Magnitude ™


12

Nonogram เป็นเกมปริศนาญี่ปุ่นที่มีเป้าหมายเพื่อวาดภาพขาวดำตามรายการของภูมิภาคที่อยู่ติดกันเช่น:

ตัวอย่าง nonogram ที่มี "แลมบ์ดา"

กำหนดขนาด nonographicของแถวหรือคอลัมน์ให้เป็นจำนวนของพื้นที่สีดำที่ต่อเนื่องกันในแถวหรือคอลัมน์นั้น ตัวอย่างเช่นแถวบนสุดมีขนาดที่ไม่ใช่ทางภูมิศาสตร์เป็น 1 เนื่องจากมีหนึ่งพื้นที่ของ 2 สี่เหลี่ยมในแถวนั้น แถวที่ 8 มีขนาดไม่เกิน 3 เนื่องจากมี 2, 2, 1

แถวหรือคอลัมน์ว่างเปล่ามีขนาดที่ไม่ใช่ทางภูมิศาสตร์เป็น 0


งานของคุณคือการเขียนโปรแกรมที่ใช้ตารางวิธีแก้ปัญหาสำหรับ nonogram และสร้างตารางวิธีการแก้ปัญหาที่มีเต็มสี่เหลี่ยมน้อยที่สุดเท่าที่เป็นไปได้ที่ทุกแถวและคอลัมน์มี magnutide nonographic เดียวกับตารางโซลูชันที่กำหนด

ตัวอย่างเช่นตารางที่ไม่ใช่สี่เหลี่ยมจัตุรัสที่เติมสี่เหลี่ยมทั้งหมดมีขนาดไม่เป็น 1 ในทุกแถวหรือคอลัมน์:

nonogram ขนาด 10x10 ที่เติมทุกช่อง

ขนาด nonographic เดียวกันสามารถทำได้เพียงแค่มีแถบเส้นทแยงมุมผ่านตารางลดจำนวนสี่เหลี่ยมที่เต็มไปอย่างมาก:

Nonogram 10x10 ที่มีขนาด nonographic เท่ากันกับด้านบน


โปรแกรมของคุณจะได้รับอินพุตซึ่งประกอบด้วย 50,000 บรรทัดจากไฟล์นี้ (1.32 MB ไฟล์ข้อความ tar.gz; 2.15 MB unzipped) แต่ละไฟล์แสดงตารางการแก้ปัญหา nonogram ขนาด 16 × 16 ที่มีสี่เหลี่ยมจัตุรัสสุ่มสีดำ (80%) และ เอาต์พุตอีก 50,000 บรรทัดโดยแต่ละตารางจะมีกริดโซลูชันที่ปรับให้เหมาะสมสำหรับกริดอินพุตที่สอดคล้องกัน

แต่ละกริดจะแสดงเป็นสตริงเบส 64 ที่มี 43 ตัวอักษร (เข้ารหัสสี่เหลี่ยมจากซ้ายไปขวาจากบนลงล่าง) จากนั้นโปรแกรมของคุณจะต้องส่งคืนเอาต์พุตในรูปแบบเดียวกัน ตัวอย่างเช่นกริดแรกในไฟล์คือE/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=และแสดงผลเป็น:

nonogram ตัวอย่างแรก

กริดเริ่มต้นด้วยการEจับคู่กับ000100ดังนั้นเซลล์หกเซลล์แรกในแถวบนสุดจะเป็นสีขาวทั้งหมดยกเว้นเซลล์ที่สี่ ตัวละครตัวถัดไปคือ/แมปที่จะไป111111ดังนั้นเซลล์ 6 เซลล์ถัดไปจะเป็นสีดำทั้งหมด - และต่อไปเรื่อย ๆ


โปรแกรมของคุณต้องส่งกลับตารางโซลูชันด้วยขนาด nonographic ที่ถูกต้องสำหรับแต่ละกรณีทดสอบ 50,000 ชุด อนุญาตให้ส่งคืนกริดเดียวกับอินพุตหากไม่พบสิ่งใดที่ดีกว่า

โปรแกรมที่จะส่งคืนสี่เหลี่ยมจัตุรัสที่เติมเต็มทั้งหมดน้อยที่สุด (ในภาษาใด ๆ ) เป็นผู้ชนะโดยมีรหัสที่สั้นกว่าคือ tiebreaker


กระดานคะแนนปัจจุบัน:

  1. 3,637,260 - Sleafar, Java
  2. 7,270,894 - ข้อบกพร่อง Matlab
  3. 10,239,288 - Joe Z. , Bash

1
ฉันไม่เห็นจุดของการเข้ารหัส 64 ฐานและทำงานกับนั่นเป็นความเจ็บปวดที่แท้จริง มันจะง่ายกว่าไหมถ้าจะสร้างแถวของคนและศูนย์? หรือเข้ารหัสสิ่งทั้งหมดเป็นบิตแมป
ข้อบกพร่อง

@flawr: มันลดขนาดไฟล์ส่วนใหญ่ (โดยปัจจัย 6 เมื่อเทียบกับเพียง 1 และ 0 ของ) นอกจากนี้บิตแมปจะยิ่งทำงานได้ยากขึ้น
Joe Z.

คุณสามารถสร้างภาพขาวดำง่ายต่อการอ่าน / เขียนและมีขนาดเท่ากับการเข้ารหัส b64
ข้อบกพร่อง

2
ยังไม่ใช่แฟนของการเข้ารหัส b64 สำหรับอินพุตและ / หรือเอาท์พุท ทำไมไม่ปล่อยให้ i / o อยู่ในรูปแบบที่สะดวก?
Sparr

1
สมมติว่าเป็นฉันควรจะมีทางออกที่ดีที่สุดในเวลานี้ในวันพรุ่งนี้
quintopia

คำตอบ:


7

Python 2 & PuLP - 2,644,688 กำลังสอง (ลดให้เหมาะสม); 10,753,553 สี่เหลี่ยม (ขยายใหญ่สุด)

golfed น้อยที่สุดถึง 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

ปรากฎว่าปัญหาเช่นสามารถแปลงเป็นโปรแกรมเชิงเส้นจำนวนเต็มได้อย่างง่ายดายและฉันต้องการปัญหาพื้นฐานเพื่อเรียนรู้วิธีใช้ PuLP - อินเทอร์เฟซหลามสำหรับนักแก้ปัญหา LP หลากหลาย - สำหรับโครงการของฉันเอง มันกลับกลายเป็นว่า PuLP นั้นใช้งานง่ายมากและตัวสร้าง LP ที่ไม่ได้รับการทำงานนั้นสมบูรณ์แบบในครั้งแรกที่ฉันลองใช้

สองสิ่งที่ดีเกี่ยวกับการใช้ตัวแก้ปัญหา IP แบบ จำกัด ขอบเขตเพื่อทำงานอย่างหนักในการแก้ปัญหานี้สำหรับฉัน (นอกเหนือจากการไม่ใช้งานตัวแก้ปัญหาแบบสาขาและแบบ จำกัด ขอบเขต) คือ

  • ตัวแก้วัตถุประสงค์ที่สร้างขึ้นนั้นรวดเร็วมาก โปรแกรมนี้แก้ปัญหาทั้งหมด 50,000 ปัญหาภายใน 17 ชั่วโมงบนพีซีที่บ้านของฉันค่อนข้างต่ำ แต่ละตัวอย่างใช้เวลาตั้งแต่ 1-1.5 วินาทีเพื่อแก้ปัญหา
  • พวกเขาผลิตโซลูชั่นที่ดีที่สุดที่รับประกันได้ (หรือบอกคุณว่าพวกเขาไม่สามารถทำได้) ดังนั้นฉันจึงมั่นใจได้ว่าจะไม่มีใครเอาชนะคะแนนของฉันในรูปสี่เหลี่ยม (แม้ว่าบางคนอาจผูกมันและเอาชนะฉันในส่วนของการเล่นกอล์ฟ)

วิธีใช้โปรแกรมนี้

ก่อนอื่นคุณจะต้องติดตั้ง PuLP pip install pulpควรทำเคล็ดลับถ้าคุณติดตั้งจุดเล็ก ๆ

จากนั้นคุณจะต้องใส่ข้อมูลต่อไปนี้ในไฟล์ชื่อ "c": https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing

จากนั้นให้รันโปรแกรมนี้ในการสร้าง Python 2 ที่ล่าช้าจากไดเรกทอรีเดียวกัน ในเวลาน้อยกว่าหนึ่งวันคุณจะมีไฟล์ชื่อ "s" ซึ่งบรรจุกริด nonogram ที่แก้ไขแล้ว 50,000 รายการ (ในรูปแบบที่อ่านได้) โดยแต่ละไฟล์มีจำนวนสี่เหลี่ยมที่เติมเต็มแสดงอยู่ด้านล่าง

หากคุณต้องการเพิ่มจำนวนสี่เหลี่ยมที่เต็มแล้วแทนให้เปลี่ยนLpMinimizeบรรทัดที่ 8 เป็นLpMaximizeแทน คุณจะได้ผลลัพธ์มากเช่นนี้: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharing

รูปแบบอินพุต

โปรแกรมนี้ใช้รูปแบบอินพุตที่แก้ไขเนื่องจาก Joe Z. กล่าวว่าเราจะได้รับอนุญาตให้เข้ารหัสรูปแบบอินพุตอีกครั้งหากเราต้องการแสดงความคิดเห็นใน OP คลิกลิงก์ด้านบนเพื่อดูว่าหน้าตาเป็นอย่างไร ประกอบด้วย 10,000 บรรทัดแต่ละบรรทัดมี 16 หมายเลข เส้นเลขคู่คือขนาดของแถวของอินสแตนซ์ที่กำหนดในขณะที่เส้นเลขคี่คือขนาดของคอลัมน์ของอินสแตนซ์เดียวกันกับบรรทัดด้านบน ไฟล์นี้สร้างขึ้นโดยโปรแกรมดังต่อไปนี้:

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 ที่ฉันสร้างขึ้นสำหรับโครงการเดียวกันที่กล่าวถึงข้างต้นมันเป็นเพียงคิวที่ข้อมูลสามารถถูกผลักเป็นลำดับของบิตหรือไบต์และข้อมูลที่สามารถ ถูกตอกขึ้นมาทีละบิตหรือไบต์ในกรณีนี้มันทำงานได้อย่างสมบูรณ์แบบ)

ฉันเข้ารหัสอินพุตอีกครั้งด้วยเหตุผลเฉพาะที่สร้าง ILP ข้อมูลเพิ่มเติมเกี่ยวกับกริดที่ใช้ในการสร้างขนาดนั้นไม่มีประโยชน์อย่างสมบูรณ์ ขนาดเป็นข้อ จำกัด เพียงอย่างเดียวดังนั้นขนาดคือทั้งหมดที่ฉันต้องการในการเข้าถึง

เครื่องมือสร้าง 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

นี่คือโปรแกรมที่สร้าง "ตัวอย่างเอาต์พุต" ที่ลิงก์ด้านบน ดังนั้นสตริงที่ยาวเป็นพิเศษในตอนท้ายของแต่ละตารางซึ่งฉันตัดทอนเมื่อเล่นกอล์ฟ (เวอร์ชัน golfed ควรสร้างผลลัพธ์ที่เหมือนกันลบด้วยคำ"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)

ฉันใช้กริด 18x18 ส่วนตรงกลาง 16x16 เป็นโซลูชันตัวต่อที่แท้จริง 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

0และs เป็นค่าไบนารีติดตามโดยcellตัวแปรที่|s เป็นค่าไบนารีติดตามโดยrowsepตัวแปรและ-s มีค่าไบนารีติดตามโดย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,""

นี่แค่กำหนดเซลล์รอบ ๆ ขอบด้านนอกของตารางให้เป็นศูนย์ นี่เป็นวิธีที่สะดวกที่สุดในการติดตามจำนวน "บล็อก" ของเซลล์ที่ถูกเติมเนื่องจากจะทำให้แน่ใจได้ว่าทุกการเปลี่ยนแปลงจากการเติมจนเต็ม (การย้ายข้ามคอลัมน์หรือแถว) จะถูกจับคู่โดยการเปลี่ยนแปลงที่สอดคล้องจากการเติมเต็มจนเต็ม ) แม้ว่าจะมีการเติมเซลล์แรกหรือเซลล์สุดท้ายในแถว นี่เป็นเหตุผลเดียวที่ใช้กริด 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 ของเซลล์และตัวคั่นโดยตรงไปทางซ้ายในแถวของมันและอยู่เหนือมันในคอลัมน์ ฉันมีข้อ จำกัด ที่จำลอง xor ภายในโปรแกรมจำนวนเต็ม {0,1} จากคำตอบที่ยอดเยี่ยมนี้: /cs//a/12118/44289

เพื่ออธิบายเพิ่มเติม: ข้อ จำกัด ของ xor นี้ทำให้ตัวคั่นสามารถเป็น 1 ถ้าหากอยู่ระหว่างเซลล์ที่เป็น 0 และ 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 สี (เกี่ยวกับเวลาในการคำนวณวิธีแก้ปัญหา) หรือไม่? ฉันได้ตอบการประกวดนั้นแล้ว (และชนะได้) อย่างไรก็ตามวิธีการของฉัน (อัลกอริธึมการค้นหา *) มอบโซลูชันที่ไม่เหมาะสมเท่านั้น
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 สี่เหลี่ยม (ขยายใหญ่สุด)

ตัวแปรทั้งสองของโปรแกรมดำเนินการซ้ำ ๆ บนกริดต้นทางโดยไม่ต้องเปลี่ยนขนาด

Minimizer

หด()

ป้อนคำอธิบายรูปภาพที่นี่

หากสี่เหลี่ยมสีดำมีเพื่อนบ้านสีขาว 2 แห่งและเพื่อนบ้านสีดำ 2 แห่งในมุม 90 °มันสามารถถูกแทนที่ด้วยสี่เหลี่ยมสีขาว

moveLine ()

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ในการกำหนดค่าเช่นด้านบนเส้นสีดำสามารถเคลื่อนย้ายไปทางขวา การทำเช่นนี้ซ้ำ ๆ กันสำหรับทั้ง 4 ทิศทางในทิศทางตามเข็มนาฬิกาและทวนเข็มนาฬิกาเพื่อเปิดโอกาสในการหดตัวแบบใหม่

Maximizer

ยกเลิกการใส่เครื่องหมายในบรรทัดmain()และใส่เครื่องหมายความคิดเห็นในบรรทัดด้านบนสำหรับรุ่นนี้

เติบโต ()

ป้อนคำอธิบายรูปภาพที่นี่

หากสี่เหลี่ยมสีขาวมีเพื่อนบ้านสีขาว 2 แห่งและเพื่อนบ้านสีดำ 2 แห่งในมุม 90 °มันสามารถถูกแทนที่ด้วยสี่เหลี่ยมสีดำ

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

Bash - 10,239,288 กำลังสอง

เป็นโซลูชันอ้างอิงล่าสุด:

cp nonograms_b64.txt solutions_b64.txt

เนื่องจากโปรแกรมของคุณได้รับอนุญาตให้ส่งคืนกริดเดียวกันหากไม่สามารถหาวิธีแก้ปัญหาที่ดีกว่าการพิมพ์คำต่อคำไฟล์ทั้งหมดจึงถูกต้องเช่นกัน

มีทั้งหมด 10,239,288 สี่เหลี่ยมจัตุรัสสีดำในไฟล์ทดสอบซึ่งค่อนข้างใกล้เคียงกับ 10,240,000 ที่คุณคาดหวังจาก 80% ของสี่เหลี่ยมจัตุรัสที่บรรจุออกมาจาก 50,000 กริดด้วย 256 ช่องสี่เหลี่ยมแต่ละอัน ตามปกติกับคำถามทดสอบแบตเตอรี่ของฉันฉันได้เลือกจำนวนกรณีทดสอบที่มีความคาดหวังว่าคะแนนที่เหมาะสมจะอยู่ในช่วง 2 ล้านถึงแม้ว่าฉันสงสัยว่าคะแนนจะใกล้เคียงกับ 4 หรือ 5 ล้านครั้งในเวลานี้ .


หากใครก็ตามสามารถสร้างวิธีแก้ปัญหาที่ทำให้สี่เหลี่ยมสีดำให้ใหญ่สุดแทนที่จะย่อให้เล็กสุดและจัดการให้ได้มากกว่า 10,240,000 ฉันอาจพิจารณาให้ความช่วยเหลือ


1

Matlab, 7,270,894 กำลังสอง (ประมาณ 71% ของต้นฉบับ)

แนวคิดเป็นการค้นหาโลภซ้ำ ๆ อย่างง่าย ๆ : สำหรับสี่เหลี่ยมสีดำทุกอันลองดูว่าคุณสามารถตั้งค่าเป็นสีขาวได้หรือไม่โดยไม่เปลี่ยนขนาดของ nonographic ทำซ้ำสองครั้ง (คุณสามารถบรรลุผลลัพธ์ที่ดีขึ้นด้วยการทำซ้ำมากขึ้น แต่ไม่ใช่ฟรี: มันส่งผลให้รันไทม์อีกต่อไปตามลำดับตอนนี้ประมาณ 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.