एक नॉनोग्राफिक मैग्नेटिट ऑप्टिमाइज़र ™ बनाएँ


12

एक नॉनोग्राम एक जापानी पहेली खेल है जिसमें लक्ष्य को सन्निहित क्षेत्रों की एक सूची के अनुसार एक काले और सफेद तस्वीर खींचना है, जैसे:

एक उदाहरण "लैम्बडा" के साथ नॉनोग्राम।

उस पंक्ति या स्तंभ में सन्निहित काले क्षेत्रों की संख्या होने के लिए किसी पंक्ति या स्तंभ के गैर-भौगोलिक परिमाण को परिभाषित करें । उदाहरण के लिए, शीर्ष पंक्ति में 1 का गैर-भौगोलिक परिमाण है, क्योंकि उस पंक्ति में 2 वर्गों का एक क्षेत्र है। 8 वीं पंक्ति में 3 का गैर-भौगोलिक परिमाण है क्योंकि इसमें 2, 2, 1 है।

एक खाली पंक्ति या स्तंभ में 0 की गैर-भौगोलिक परिमाण है।


आपका कार्य एक प्रोग्राम लिखना है जो एक नॉनोग्राम के लिए एक समाधान ग्रिड लेता है, और एक समाधान ग्रिड को यथासंभव भरे हुए वर्गों के साथ बनाता है जहां हर पंक्ति और स्तंभ में दिए गए समाधान ग्रिड के समान नॉनोग्राफिक मैगनेट होता है।

उदाहरण के लिए, भरे हुए सभी वर्गों के साथ एक नॉनोग्राम ग्रिड में प्रत्येक पंक्ति या स्तंभ पर 1 का गैर-भौगोलिक परिमाण है:

एक 10x10 नॉनोग्राम जहां हर वर्ग को भरा जाता है।

ग्रिड के माध्यम से विकर्ण पट्टी होने से उसी गैर-भौगोलिक परिमाण को प्राप्त किया जा सकता है, नाटकीय रूप से भरे हुए वर्गों की संख्या को कम करके:

उपरोक्त के रूप में एक ही नॉनोग्राफिक परिमाण के साथ एक 10x10 नॉनोग्राम।


आपके प्रोग्राम को इस फ़ाइल से 50,000 पंक्तियों से युक्त एक इनपुट प्राप्त होगा (1.32 MB tar.gz text file; 2.15 MB अनज़िप्ड), प्रत्येक एक एकल 16 × 16 नॉनोग्राम सॉल्यूशन ग्रिड को बेतरतीब ढंग से (80% ब्लैक) भरे हुए इन-स्क्वॉयर, और एक और 50,000 लाइनों का उत्पादन, प्रत्येक इनपुट इनपुट ग्रिड के लिए अनुकूलित समाधान ग्रिड युक्त।

प्रत्येक ग्रिड को 43 अक्षरों के साथ बेस 64 स्ट्रिंग के रूप में दर्शाया गया है (बाएं से दाएं, फिर ऊपर से नीचे तक एन्कोडिंग), और आपके प्रोग्राम को उसी प्रारूप में अपना आउटपुट वापस करने की आवश्यकता होगी। उदाहरण के लिए, फ़ाइल में पहला ग्रिड E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=निम्नानुसार है:

पहला उदाहरण नॉनोग्राम

ग्रिड Eकिन मानचित्रों से शुरू होता है 000100, इसलिए शीर्ष पंक्ति में पहली छह कोशिकाएं चौथे को छोड़कर सभी सफेद हैं। अगला चरित्र /जो नक्शे के लिए है 111111, इसलिए अगली 6 कोशिकाएं सभी काले हैं - और इसी तरह।


आपके कार्यक्रम को वास्तव में 50,000 टेस्ट मामलों में से प्रत्येक के लिए सही नॉनोग्राफिक परिमाण के साथ एक समाधान ग्रिड वापस करना होगा। यदि कुछ बेहतर नहीं मिला तो इनपुट के रूप में उसी ग्रिड को वापस करने की अनुमति है।

कम से कम कुल भरे हुए वर्गों (किसी भी भाषा में) को वापस करने का कार्यक्रम विजेता होता है, जिसमें छोटा कोड टाईब्रेकर होता है।


वर्तमान स्कोरबोर्ड:

  1. 3,637,260 - स्लिफ़र, जावा
  2. 7,270,894 - दोष, मतलाब
  3. 10,239,288 - जो जेड, बैश

1
मैं वास्तव में आधार 64 एनकोडिंग के बिंदु को नहीं देखता हूं और इसके साथ काम करना एक वास्तविक दर्द है। क्या यह आसान नहीं होगा कि आप केवल अपनी और शून्य रेखाएँ बनाएँ? या बिटमैप के रूप में पूरी चीज़ को एनकोड करें?
flawr

@flawr: यह फ़ाइलों को कम करता है, मुख्य रूप से (केवल 1 और 0 की तुलना में 6 के कारक द्वारा)। इसके अलावा, बिटमैप्स के साथ काम करना और भी कठिन होगा।
जो जे।

आप बस एक काले और सफेद छवि बना सकते हैं, पढ़ने में आसान / लिख सकते हैं और बी 64 एन्कोडिंग के समान आकार।
दोष

2
इनपुट और / या आउटपुट के लिए भी b64 एन्कोडिंग का प्रशंसक नहीं है। सिर्फ i / o को किसी भी सुविधाजनक प्रारूप में क्यों नहीं रखा जाए?
Sparr

1
यह मानते हुए, मुझे कल इस समय तक एक इष्टतम समाधान होना चाहिए।
क्विंटोपिया

जवाबों:


7

पायथन 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

(एनबी: भारी इंडेंटेड लाइनें टैब से शुरू होती हैं, स्पेस से नहीं।)

उदाहरण आउटपुट: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing

यह पता चलता है कि जैसे समस्याएं आसानी से पूर्णांक कार्यक्रम के लिए परिवर्तनीय हैं, और मुझे एलएल सॉल्वर की एक किस्म के लिए PuLP- एक अजगर इंटरफ़ेस का उपयोग करने के लिए सीखने की एक बुनियादी समस्या की आवश्यकता थी - मेरी खुद की एक परियोजना के लिए। यह भी पता चला है कि PuLP उपयोग करने के लिए बेहद आसान है, और ungolfed LP बिल्डर ने पूरी तरह से पहली बार काम किया जब मैंने इसे आज़माया।

एक शाखा-और-बाउंड आईपी सॉल्वर को नियोजित करने के बारे में दो अच्छी बातें मेरे लिए इसे सुलझाने का कठिन काम करने के लिए (एक शाखा और बाध्य सॉल्वर को लागू नहीं करने के अलावा) हैं कि

  • उद्देश्य-निर्मित सॉल्वर वास्तव में तेज़ हैं। यह कार्यक्रम मेरे अपेक्षाकृत कम अंत वाले होम पीसी पर लगभग 50000 समस्याओं को हल करता है। प्रत्येक उदाहरण को हल करने में 1-1.5 सेकंड का समय लगा।
  • वे गारंटीकृत इष्टतम समाधान का उत्पादन करते हैं (या आपको बताते हैं कि वे ऐसा करने में विफल रहे)। इस प्रकार, मुझे विश्वास हो सकता है कि कोई भी मेरे स्कोर को वर्गों में नहीं हराएगा (हालाँकि कोई इसे बाँध सकता है और मुझे गोल्फ भाग पर हरा सकता है)।

इस कार्यक्रम का उपयोग कैसे करें

सबसे पहले, आपको PuLP इंस्टॉल करना होगा। pip install pulpयदि आपने पाइप स्थापित किया है तो चाल चलनी चाहिए।

फिर, आपको "c" नामक एक फ़ाइल में निम्नलिखित डालने की आवश्यकता होगी: https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=singing

फिर, एक ही निर्देशिका से किसी भी दिवंगत पायथन 2 बिल्ड में इस कार्यक्रम को चलाएं। एक दिन से भी कम समय में, आपके पास "s" नामक एक फ़ाइल होगी जिसमें 50,000 हल किए गए नॉनोग्राम ग्रिड (पठनीय प्रारूप में) होते हैं, जिनमें से प्रत्येक में नीचे भरे हुए कुल वर्ग होते हैं।

यदि आप इसके बजाय भरे हुए वर्गों की संख्या को अधिकतम करना चाहते हैं, तो LpMinimizeपंक्ति 8 को LpMaximizeइसके स्थान पर बदलें । आपको आउटपुट बहुत पसंद आएगा: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharing

इनपुट प्रारूप

यह प्रोग्राम एक संशोधित इनपुट प्रारूप का उपयोग करता है, क्योंकि जो जेड ने कहा कि अगर हमें ओपी पर एक टिप्पणी पसंद है तो हमें इनपुट प्रारूप को फिर से एनकोड करने की अनुमति होगी। उपरोक्त लिंक पर क्लिक करके देखें कि यह कैसा दिखता है। इसमें 10000 रेखाएँ होती हैं, जिनमें से प्रत्येक में 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

(इस पुन: एन्कोडिंग प्रोग्राम ने मुझे अपने कस्टम बिटक्वायु वर्ग का परीक्षण करने का एक अतिरिक्त अवसर भी दिया, जो मैंने ऊपर उल्लेखित एक ही परियोजना के लिए बनाया है। यह केवल एक कतार है, जिसमें डेटा को बिट्स या बाइट्स के अनुक्रम के रूप में धकेला जा सकता है, और किस डेटा से। एक समय में या तो एक बिट या बाइट को पॉपअप किया जाता है। इस उदाहरण में, यह पूरी तरह से काम करता है।)

मैंने उस विशिष्ट कारण के लिए इनपुट को फिर से एनकोड किया जो कि 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

यह वह प्रोग्राम है जो वास्तव में ऊपर लिंक किए गए "उदाहरण आउटपुट" का उत्पादन करता है। इसलिए प्रत्येक ग्रिड के अंत में अतिरिक्त लंबे तार, जिन्हें मैंने इसे गोल्फिंग करते समय काट दिया। (गोल्फ संस्करण को समान आउटपुट का उत्पादन करना चाहिए, शब्दों को घटाकर "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 बाइनरी वैरिएबल बनाती है: "सेल_0_0", "सेल_0_1", और इसी तरह। मैं ग्रिड के समाधान भाग में कोशिकाओं के बीच और आसपास "रिक्त स्थान" के ग्रिड भी बनाता हूं। rowseps289 चर को इंगित करता है जो रिक्त स्थान को अलग-अलग करने वाले रिक्त स्थान का प्रतीक है, जबकि 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और रों ने उन्हें ट्रैक बाइनरी मान हैं 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,""

यह बस ग्रिड के बाहरी किनारे के चारों ओर की कोशिकाओं को शून्य पर सेट करता है (यही कारण है कि मैंने उन्हें ऊपर शून्य के रूप में दर्शाया है)। यह कोशिकाओं के कितने "ब्लॉक" को ट्रैक करने का सबसे समीचीन तरीका है, क्योंकि यह सुनिश्चित करता है कि अनफ़िल्टर्ड से भरा हुआ (कॉलम या पंक्ति के पार ले जाने वाला) से प्रत्येक परिवर्तन को भरे हुए (और इसके विपरीत से भरे हुए परिवर्तन से मेल खाता है) ), भले ही पंक्ति में पहला या अंतिम सेल भरा हो। यह पहली जगह में 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 के तर्क का वास्तविक मांस है। मूल रूप से यह आवश्यक है कि प्रत्येक सेल (पहली पंक्ति और स्तंभ के अलावा) सेल का तार्किक एक्सोर हो और विभाजक सीधे अपनी पंक्ति में बाईं ओर और सीधे उसके स्तंभ में ऊपर। मुझे इस अद्भुत उत्तर से {0,1} पूर्णांक कार्यक्रम के भीतर एक एक्सोर का अनुकरण करने वाली अड़चनें मिलीं: /cs//a/12118/44289

थोड़ा और समझाने के लिए: यह एक्सोर बाधा बनाता है ताकि विभाजक 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 को हल करने के लिए कहता है, फिर परिणामी समाधान को प्रारूपित करता है क्योंकि यह फ़ाइल में लिखता है।


वास्तव में अच्छा जवाब। मुझे एलपी सॉल्वर्स के बारे में जानना चाहते हैं। क्या आपको लगता है कि इसका उपयोग 19x19, 6 रंगों के बोर्ड (समाधान की गणना करने के लिए समय के बारे में ) के लिए बाढ़ की पहेली (लिंक) को हल करने के लिए किया जा सकता है ? मैंने पहले ही जवाब दिया है कि प्रतियोगिता (और इसे जीता) हालांकि मेरी विधि (ए * सर्च एल्गोरिथ्म) केवल गैर इष्टतम समाधान देती है।
टिगोर

@tigrou धन्यवाद मुझे यकीन नहीं है कि बाढ़ समस्या ऐसे समाधान को स्वीकार करने के लिए पर्याप्त रैखिक है। मैं निश्चित रूप से यह नहीं देख सकता कि इसे इस तरह से कैसे बनाया जाए।
क्विंटोपिया

ऐसा लगता है कि किसी ने पहले ही यह कोशिश कर ली थी: कुनिगामी.ब्लॉग / 2012 / 09 / 16 / flood- it- an-exact-approach हालांकि वे एक 14x14 बोर्ड के लिए संभव समय में इष्टतम समाधान नहीं कर सके।
tigrou

3

जावा, 6,093,092 4,332,656 3,637,260 वर्ग (न्यूनतम), 10,567,550 10,567,691 10,568,746 वर्ग (अधिकतम)

कार्यक्रम के दोनों संस्करण बार-बार स्रोत ग्रिड पर परिचालनों को बदलने के बिना संचालन करते हैं।

Minimizer

हटना ()

यहाँ छवि विवरण दर्ज करें

यदि एक काले वर्ग में 2 सफेद पड़ोसी और 90 ° कोण में 2 काले पड़ोसी हैं, तो इसे एक सफेद वर्ग द्वारा प्रतिस्थापित किया जा सकता है।

moveLine ()

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

काली रेखा के ऊपर के विन्यास में दाईं ओर ले जाया जा सकता है। यह सभी 4 लाइन दिशाओं दक्षिणावर्त और वामावर्त के लिए बार-बार किया जाता है, ताकि नई सिकुड़न संभावनाओं को खोला जा सके।

दल

main()इस संस्करण के लिए लाइन को अंदर और बाहर की रेखा से हटा दें ।

बढ़ना()

यहाँ छवि विवरण दर्ज करें

यदि किसी सफेद वर्ग में 90 ° कोण में 2 सफेद पड़ोसी और 2 काले पड़ोसी हैं, तो इसे काले वर्ग द्वारा प्रतिस्थापित किया जा सकता है।

moveLine ()

मिनिमाइज़र के समान।

स्रोत

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 काले वर्ग हैं, जो कि 10,240,000 के बहुत करीब है, आप 50,000 वर्ग में से प्रत्येक के साथ भरे हुए वर्गों में से 80% वर्गों की अपेक्षा करेंगे, जिनमें से प्रत्येक में 256 वर्ग होंगे। अपने टेस्ट-बैटरी प्रश्नों के साथ, मैंने इस अपेक्षा के साथ परीक्षण मामलों की संख्या का चयन किया है कि इष्टतम स्कोर 2-मिलियन रेंज के आसपास होंगे, हालांकि मुझे संदेह है कि इस बार स्कोर 4 या 5 मिलियन के करीब होगा ।


यदि कोई ऐसा समाधान बना सकता है जो काले वर्गों को कम से कम करने के बजाय अधिकतम करता है और 10,240,000 से अधिक प्राप्त करने का प्रबंधन करता है, तो मैं इसे एक इनाम देने पर विचार कर सकता हूं।


1

मतलाब, 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])
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.