एक टॉप-फ्रंट-साइड पहेली सॉल्वर बनाएँ


15

एक टॉप-फ्रंट-साइड पहेली एक पहेली है, जहाँ आपको तीन-ऑर्थोगोनल व्यू दिए गए (आमतौर पर क्यूबिक) ब्लॉक के 3-डी आकार के निर्माण की आवश्यकता होती है: एक टॉप व्यू, एक फ्रंट व्यू और एक साइड व्यू।

उदाहरण के लिए, एक शीर्ष, सामने और साइड व्यू इस प्रकार दिया गया है:

Top:        Front:      Side:
. . . .     . . . .     . . . .
. x x .     . x x .     . x x .
. x x .     . x x .     . x x .
. . . .     . . . .     . . . .

In this problem, the side view is taken from the right.

एक 2x2x2 क्यूब (वॉल्यूम 8 के साथ) इस समाधान को पूरा करेगा, लेकिन यह वॉल्यूम 4 में उल्लेखनीय है, अगर हमारे पास निम्न परत है:

. . . .     . . . .
. x . .     . . x .
. . x .     . x . .
. . . .     . . . .

कुछ नायाब व्यवस्थाएं भी हैं। उदाहरण के लिए:

Top:        Front:      Side:
. . . .     . . . .     . . . .
. . . .     . . x .     . . . .
. x . .     . . . .     . x . .
. . . .     . . . .     . . . .

यदि शीर्ष दृश्य कहता है कि ब्लॉक बाईं ओर से दूसरा है, तो ऐसा कोई तरीका नहीं है जो सामने के दृश्य से मेल खा सके जो कहता है कि ब्लॉक बाईं ओर से तीसरा होना चाहिए। इसलिए यह व्यवस्था असंभव है।


आपका कार्य एक ऐसा प्रोग्राम बनाना है, जो मनमाने ढंग से 4x4 टॉप-फ्रंट-साइड की पहेली को देखते हुए, इसे कम से कम क्यूब्स में हल करने का प्रयास करता है, या इसे अयोग्य घोषित करता है।

आपका कार्यक्रम इनपुट के रूप में 48 बिट्स की एक श्रृंखला के रूप में ले जाएगा, शीर्ष, सामने और साइड व्यू का प्रतिनिधित्व करेगा। वे किसी भी प्रारूप में हो सकते हैं जो आप चाहते हैं (6-बाइट स्ट्रिंग, 0 की स्ट्रिंग और 1 का, 12-अंकीय हेक्स संख्या, आदि), लेकिन बिट्स का क्रम इस तरह से मैप करना चाहिए:

Top: 0x00   Front: 0x10 Side: 0x20
0 1 2 3     0 1 2 3     0 1 2 3
4 5 6 7     4 5 6 7     4 5 6 7
8 9 a b     8 9 a b     8 9 a b
c d e f     c d e f     c d e f

दूसरे शब्दों में, बिट्स बाएं से दाएं, ऊपर से नीचे के क्रम में, ऊपर, फिर सामने, फिर साइड व्यू में जाते हैं।

आपका प्रोग्राम तब 4x4x4 ग्रिड में क्यूब्स को इंगित करते हुए 64 बिट्स की एक श्रृंखला का उत्पादन करेगा, जो कि भरे हुए हैं या इंगित करते हैं कि ग्रिड बेकार है।


आपके प्रोग्राम को 1,000,000 परीक्षण मामलों की बैटरी चलाकर चलाया जाएगा।

परीक्षण डेटा को "999999" के माध्यम से पूर्णांक "000000" के एमडी 5 हैश को स्ट्रिंग्स के रूप में उत्पन्न किया जाएगा, इनमें से प्रत्येक हैश के पहले 48 बिट्स (12 हेक्सिट) को निकाला जाएगा, और शीर्ष-सामने के इनपुट के रूप में उनका उपयोग किया जाएगा। पक्ष पहेली। एक उदाहरण के रूप में, यहां कुछ परीक्षण इनपुट और उनके द्वारा बनाई गई पहेलियाँ हैं:

Puzzle seed: 000000   hash: 670b14728ad9
Top:        Front:      Side:
. x x .     . . . x     x . . .
x x x x     . x . x     x . x .
. . . .     . x x x     x x . x
x . x x     . . x .     x . . x

Puzzle seed: 000001   hash: 04fc711301f3
Top:        Front:      Side:
. . . .     . x x x     . . . .
. x . .     . . . x     . . . x
x x x x     . . . x     x x x x
x x . .     . . x x     . . x x

Puzzle seed: 000157   hash: fe88e8f9b499
Top:        Front:      Side:
x x x x     x x x .     x . x x
x x x .     x . . .     . x . .
x . . .     x x x x     x . . x
x . . .     x . . x     x . . x

पहले दो असाध्य हैं, जबकि अंतिम में निम्नलिखित परतों के साथ एक समाधान है, आगे पीछे:

x . . .   . . . .   x x x .   x x x .
. . . .   x . . .   . . . .   . . . .
x . . .   . . . .   . . . .   x x x x
x . . .   . . . .   . . . .   x . . x

There are a total of 16 blocks here, but it can probably be done in less.

प्राथमिकता के अवरोही क्रम में आपके कार्यक्रम का स्कोर निम्नलिखित मानदंडों द्वारा निर्धारित किया जाएगा:

  • हल किए गए मामलों की उच्चतम संख्या।
  • उन मामलों को हल करने के लिए आवश्यक ब्लॉक की सबसे कम संख्या।
  • बाइट्स में सबसे छोटा कोड।

आपको अपने द्वारा स्कोर जमा करना होगा और गणना करनी होगी, जिसके लिए आपके प्रोग्राम को सभी 1,000,000 परीक्षण मामलों के माध्यम से चलाने में सक्षम होना चाहिए।


मैंने इस समस्या के लिए परीक्षण मामलों को उत्पन्न करने की कोशिश करते हुए सीखा कि अधिक मामले न के बराबर हैं। मुझे आश्चर्य है कि यह कैसे निकलेगा।
जो जेड

यदि यह एक अनुकूलन समस्या है, तो एक समय सीमा होनी चाहिए, इसलिए लोग इसे बाध्य नहीं करते हैं।
isaacg

समय सीमा हालांकि वे इसे परीक्षण करने के लिए लंबे समय तक है। एक समाधान जो हमेशा के लिए चलता है वह कभी भी एक परिणाम नहीं देगा जो यहां पोस्ट किया जा सकता है। यही कारण है कि मैं लिखने की सभी अनुकूलन समस्याओं।
जो जेड।


1
@JoeZ। orlp सही है। मैं अपने md5-to-पहेली रूपांतरण में बग था। 00000-99999 में 551 सॉल्वेबल पज़ल्स और 000000-999999 में 5360 सॉल्वेबल पज़ल्स।
जकुबे

जवाबों:


5

पायथन: 69519 ब्लॉकों, 594 बाइट्स का उपयोग करके 5360 परीक्षण-मामले हल किए गए

यह इष्टतम मान होना चाहिए।

दृष्टिकोण:

अगर टेस्ट-केस वास्तविक सॉल्व है तो पहले मैं टेस्ट करूंगा। मैं ऐसा करने के लिए 64 की लंबाई की सूची को शुरू करता हूं और सभी पदों को शून्य पर सेट करता हूं, अगर तीन विचारों के अनुरूप पिक्सेल शून्य है। फिर मैं सभी 3 दिशाओं से पहेली को सरल रूप से देखता हूं, और देखें कि क्या दृश्य इनपुट विचारों के समान हैं। यदि समान हैं, तो पहेली हल करने योग्य है (हम पहले से ही सबसे खराब समाधान पा चुके हैं), अन्यथा यह बेकार है।

तब मैं इष्टतम समाधान खोजने के लिए एक शाखा और बाध्य दृष्टिकोण करता हूं।

शाखायुक्त: मेरे पास एक पुनरावर्ती कार्य है। पुनरावृत्ति की गहराई बताती है कि कितने मूल्य पहले से तय हैं। फ़ंक्शन के प्रत्येक कॉल में मैं खुद को दो बार कॉल करता हूं, यदि वर्तमान इंडेक्स पर मूल्य 1 है (यह इष्टतम समाधान में 0 या 1 हो सकता है), या एक बार, यदि मान 0 है (इसे शून्य होना चाहिए) सर्वोतम उपाय)।

बाउंडिंग: मैं यहां दो अलग-अलग रणनीतियों का उपयोग करता हूं।

  1. मैं प्रत्येक फ़ंक्शन कॉल में 3 पक्षों से विचारों की गणना करता हूं और देखें कि क्या वे अभी भी इनपुट मूल्यों से मेल खाते हैं। यदि वे मेल नहीं खाते हैं, तो मैं फ़ंक्शन को पुनरावर्ती नहीं कहता हूं।

  2. मैं स्मृति में सबसे अच्छा समाधान रखता हूं। यह वर्तमान शाखा में पहले से अधिक निश्चित लोगों की तुलना में सबसे अच्छा समाधान है, मैं पहले से ही उस शाखा को बंद कर सकता हूं। इसके अतिरिक्त मैं सक्रिय ब्लॉकों की संख्या के लिए कम बाध्यता का अनुमान लगा सकता हूं, जो तय नहीं हैं। और हालत बन जाती है#number of activated fixed blocks + #lower bound of activated blocks (under the not fixed blocks) < #number of activated blocks in the best solution.

यहाँ पायथन कोड है। यह एक फ़ंक्शन को परिभाषित करता है, fजो 1s और 0s वाली 3 सूचियों की अपेक्षा करता है और 0 (ना कि सॉल्वेबल) या 1s और 0s की सूची को इष्टतम समाधान का प्रतिनिधित्व करता है।

S=sum;r=range
def f(t,f,s):
 for i in r(4):s[4*i:4*i+4]=s[4*i:4*i+4][::-1]
 c=[min(t[i%16],f[(i//16)*4+i%4],s[i//4])for i in r(64)]
 m=lambda:([int(S(c[i::16])>0)for i in r(16)],[int(S(c[i+j:i+j+16:4])>0)for i in r(0,64,16)for j in r(4)],[int(S(c[i+j:i+j+4])>0)for i in r(0,64,16)for j in r(0,16,4)])==(t,f,s)
 Z=[65,0];p=[]
 def g(k):
  if k>63and S(c)<Z[0]:Z[:]=[S(c),c[:]]
  if k>63or S(c[:k])+p[k]>=Z[0]:return
  if c[k]:c[k]=0;m()and g(k+1);c[k]=1
  m()and g(k+1)
 for i in r(64):h,R=(i//16)*4+4,(i//4)%4;p+=[max(S(f[h:]),S(s[h:]))+max((R<1)*S(f[h+i%4-3:h]),S(s[h+R-3:h]))]
 g(0);return Z[1]

कोड की लंबाई 596 बाइट्स / वर्ण है। और यहाँ परीक्षण रूपरेखा है:

from hashlib import md5
from time import time

N = 1000000
start=time();count=blocks=0
for n in range(N):
 bits = list(map(int,"{:048b}".format(int(md5("{:06}".format(n).encode("utf-8")).hexdigest()[:12], 16))))
 result = f(bits[:16], bits[16:32], bits[32:])
 if result:
  count += 1
  blocks += sum(result)
  print("Seed: {:06}, blocks: {}, cube: {}".format(n, sum(result), result))
print()
print("{} out of {} puzzles are solvable using {} blocks.".format(count, N, blocks))
print("Total time: {:.2f} seconds".format(time()-start))

आप यहां कार्यक्रम का एक अनगुल्ड संस्करण पा सकते हैं । यह थोड़ा तेज भी है।

परिणाम:

1000000 पज़ल्स में से 5360 सॉल्व हैं। कुल में हमें 69519 ब्लॉक चाहिए। ब्लॉक की संख्या 6 ब्लॉक से 18 ब्लॉक तक भिन्न होती है। सबसे कठिन पहेली को हल करने में लगभग 1 सेकंड का समय लगा। यह बीज के साथ पहेली है "347177", जो दिखता है

Top:      Front:    Side:
x x . .   x x x x   x . x .
x x x x   x x x x   x x x x
x x x x   x x x x   x x x x
x x . x   x x x x   x . x x

और 18 क्यूब्स के साथ एक इष्टतम समाधान है। निम्न में से प्रत्येक परत के लिए ऊपर से कुछ है:

Top 1:    Top 2:    Top 3:    Top 4:
. . . .   . x . .   . x . .   x . . .
. . x x   . . x .   x . . .   . x x .
. . . .   . . . x   x x x .   . . . .
x x . .   x . . .   . . . x   . . . x

सभी परीक्षण-मामलों के लिए कुल चलने का समय लगभग 90 सेकंड था। मैंने अपने कार्यक्रम को निष्पादित करने के लिए PyPy का उपयोग किया। CPython (डिफ़ॉल्ट पायथन इंटरप्रेटर) थोड़ा धीमा है, लेकिन सभी पहेलियों को केवल 7 मिनट में हल करता है।

आप यहां संपूर्ण आउटपुट पा सकते हैं : आउटपुट स्व-व्याख्यात्मक है। उदाहरण के लिए उपरोक्त पहेली का आउटपुट है:

Seed: 347177, blocks: 18, cube: [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1]

3

69519 ब्लॉकों के साथ 5360 मामले हल हुए; 923 बाइट्स

यह भी इष्टतम है। लोगों और शून्य की एक सरणी के साथ कॉल करें। NullPointerExceptionअमान्य इनपुट के लिए फेंकता है । कुछ दक्षता यह गोल्फ के लिए बलिदान किया है। यह अभी भी सभी 1000000 परीक्षण इनपुट के लिए एक उचित समय के भीतर पूरा होता है।

import java.util.*;int[]a(int[]a){a b=new a(a);b=b.b(64);return b.d();}class a{List<int[]>a=new ArrayList();List b=new ArrayList();int c;a(int[]d){int e=0,f,g,h,i[]=new int[48];for(;e<64;e++){f=e/16;g=(e%16)/4;h=e%4;if(d[f+12-4*h]+d[16+f+g*4]+d[32+h+g*4]>2){i[f+12-4*h]=i[16+f+g*4]=i[32+h+g*4]=1;a.add(new int[]{f,g,h});c++;}}if(!Arrays.equals(d,i))throw null;b=f();}a(){}a b(int d){if(c-b.size()>d|b.size()<1)return this;a e=c();e.a.remove(b.get(0));e.b.retainAll(e.f());e.c--;e=e.b(d);d=Math.min(e.c,d);a f=c();f=f.b(d);return e.c>f.c?f:e;}a c(){a c=new a();c.a=new ArrayList(a);c.b=new ArrayList(b);c.b.remove(0);c.c=this.c;return c;}int[]d(){int[]d=new int[64];for(int[]e:a)d[e[2]*16+e[1]*4+e[0]]=1;return d;}List f(){List d=new ArrayList();int e,f,g;for(int[]h:a){e=0;f=0;g=0;for(int[]p:a)if(p!=h){e|=h[0]==p[0]&h[1]==p[1]?1:0;f|=h[0]==p[0]&h[2]==p[2]?1:0;g|=h[1]==p[1]&h[2]==p[2]?1:0;}if(e+f+g>2)d.add(h);}return d;}}

रणनीति:

मैं वास्तव में इस पहेली को बहुत थोड़ा खेलता था जब मैं 10. था। यह मेरे दृष्टिकोण का उपयोग करता है।

चरण 1:

सबसे अधिक ब्लॉक के साथ क्यूब को फॉर्म करें जो दिए गए विचारों को फिट करता है।

चरण 2:

हटाने योग्य टुकड़ों की एक सूची बनाएं। (किसी भी टुकड़े के साथ उस पंक्ति में एक और टुकड़ा होता है, उसके स्तंभ, और उसके बीम में।)

चरण 3:

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

चरण 4:

सबसे अच्छा वैध घन रखें।

Ungolfed:

int[] main(int[] bits) {
    Cube cube = new Cube(bits);
    cube = cube.optimize(64);
    return cube.bits();
}

class Cube {

    List<int[]> points = new ArrayList();
    List removablePoints = new ArrayList();
    int size;

    Cube(int[] bits) {
        int i = 0,x,y,z,placed[] = new int[48];
        for (; i < 64; i++) {
            x = i / 16;
            y = (i % 16) / 4;
            z = i % 4;
            if (bits[x + 12 - 4 * z] + bits[16 + x + y * 4] + bits[32 + z + y * 4] > 2) {
                placed[x + 12 - 4 * z] = placed[16 + x + y * 4] = placed[32 + z + y * 4] = 1;
                points.add(new int[]{x, y, z});
                size++;
            }
        }

        if (!Arrays.equals(bits, placed))
            throw null;

        removablePoints = computeRemovablePoints();
    }

    Cube() {
    }

    Cube optimize(int smallestFound) {
        if (size - removablePoints.size() > smallestFound | removablePoints.size() < 1)
            return this;

        Cube cube1 = duplicate();
        cube1.points.remove(removablePoints.get(0));

        cube1.removablePoints.retainAll(cube1.computeRemovablePoints());
        cube1.size--;

        cube1 = cube1.optimize(smallestFound);
        smallestFound = Math.min(cube1.size, smallestFound);

        Cube cube2 = duplicate();

        cube2 = cube2.optimize(smallestFound);

        return cube1.size > cube2.size ? cube2 : cube1;

    }

    Cube duplicate() {
        Cube c = new Cube();
        c.points = new ArrayList(points);
        c.removablePoints = new ArrayList(removablePoints);
        c.removablePoints.remove(0);
        c.size = size;
        return c;
    }

    int[] bits() {
        int[] bits = new int[64];
        for (int[] point : points)
            bits[point[2] * 16 + point[1] * 4 + point[0]] = 1;
        return bits;
    }

    List computeRemovablePoints(){
        List removablePoints = new ArrayList();
        int removableFront, removableTop, removableSide;
        for (int[] point : points) {
            removableFront = 0;
            removableTop = 0;
            removableSide = 0;
            for (int[] p : points)
                if (p != point) {
                    removableFront |= point[0] == p[0] & point[1] == p[1] ? 1 : 0;
                    removableTop |= point[0] == p[0] & point[2] == p[2] ? 1 : 0;
                    removableSide |= point[1] == p[1] & point[2] == p[2] ? 1 : 0;
                }
            if (removableFront + removableTop + removableSide > 2)
                removablePoints.add(point);
        }
        return removablePoints;
    }

    public String toString() {

        String result = "";
        int bits[] = bits(),x,y,z;

        for (z = 0; z < 4; z++) {
            for (y = 0; y < 4; y++) {
                for (x = 0; x < 4; x++) {
                    result += bits[x + 4 * y + 16 * z] > 0 ? 'x' : '.';
                }
                result += System.lineSeparator();
            }
            result += System.lineSeparator();
        }

        return result;

    }
}

पूरा कार्यक्रम:

import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Example cube:
 *
 * origin
 * |   ........
 * |  .      ..
 * | . top  . .
 * v.      .  .
 * ........   .  <- side
 * .      .  .
 * . front. .
 * .      ..
 * ........
 *
 *     / z
 *    /
 *  /
 * .-----> x
 * |
 * |
 * |
 * V y
 */

public class PPCG48247 {

    public static void main(String[] args) throws Exception{
        MessageDigest digest = MessageDigest.getInstance("MD5");
        int totalSolved = 0;
        int totalCubes = 0;

        for (int i = 0; i < 1000000; i++){
            byte[] input = String.format("%06d", i).getBytes();

            byte[] hash = digest.digest(input);
            int[] bits = new int[48];

            for (int j = 0, k = 0; j < 6; j++, k += 8){
                byte b = hash[j];
                bits[k] = (b >> 7) & 1;
                bits[k + 1] = (b >> 6) & 1;
                bits[k + 2] = (b >> 5) & 1;
                bits[k + 3] = (b >> 4) & 1;
                bits[k + 4] = (b >> 3) & 1;
                bits[k + 5] = (b >> 2) & 1;
                bits[k + 6] = (b >> 1) & 1;
                bits[k + 7] = b & 1;
            }

            try {
                int[] solution = new PPCG48247().a(bits);
                totalSolved++;
                for (int b : solution){
                    totalCubes += b;
                }
            } catch (NullPointerException ignored){}

        }
        System.out.println(totalSolved);
        System.out.println(totalCubes);
    }

    int[] main(int[] bits) {
        Cube cube = new Cube(bits);
        cube = cube.optimize(64);
        return cube.bits();
    }

    class Cube {

        List<int[]> points = new ArrayList();
        List removablePoints = new ArrayList();
        int size;

        Cube(int[] bits) {
            int i = 0,x,y,z,placed[] = new int[48];
            for (; i < 64; i++) {
                x = i / 16;
                y = (i % 16) / 4;
                z = i % 4;
                if (bits[x + 12 - 4 * z] + bits[16 + x + y * 4] + bits[32 + z + y * 4] > 2) {
                    placed[x + 12 - 4 * z] = placed[16 + x + y * 4] = placed[32 + z + y * 4] = 1;
                    points.add(new int[]{x, y, z});
                    size++;
                }
            }

            if (!Arrays.equals(bits, placed))
                throw null;

            removablePoints = computeRemovablePoints();
        }

        Cube() {
        }

        Cube optimize(int smallestFound) {
            if (size - removablePoints.size() > smallestFound | removablePoints.size() < 1)
                return this;

            Cube cube1 = duplicate();
            cube1.points.remove(removablePoints.get(0));

            cube1.removablePoints.retainAll(cube1.computeRemovablePoints());
            cube1.size--;

            cube1 = cube1.optimize(smallestFound);
            smallestFound = Math.min(cube1.size, smallestFound);

            Cube cube2 = duplicate();

            cube2 = cube2.optimize(smallestFound);

            return cube1.size > cube2.size ? cube2 : cube1;

        }

        Cube duplicate() {
            Cube c = new Cube();
            c.points = new ArrayList(points);
            c.removablePoints = new ArrayList(removablePoints);
            c.removablePoints.remove(0);
            c.size = size;
            return c;
        }

        int[] bits() {
            int[] bits = new int[64];
            for (int[] point : points)
                bits[point[2] * 16 + point[1] * 4 + point[0]] = 1;
            return bits;
        }

        List computeRemovablePoints(){
            List removablePoints = new ArrayList();
            int removableFront, removableTop, removableSide;
            for (int[] point : points) {
                removableFront = 0;
                removableTop = 0;
                removableSide = 0;
                for (int[] p : points)
                    if (p != point) {
                        removableFront |= point[0] == p[0] & point[1] == p[1] ? 1 : 0;
                        removableTop |= point[0] == p[0] & point[2] == p[2] ? 1 : 0;
                        removableSide |= point[1] == p[1] & point[2] == p[2] ? 1 : 0;
                    }
                if (removableFront + removableTop + removableSide > 2)
                    removablePoints.add(point);
            }
            return removablePoints;
        }

        public String toString() {

            String result = "";
            int bits[] = bits(),x,y,z;

            for (z = 0; z < 4; z++) {
                for (y = 0; y < 4; y++) {
                    for (x = 0; x < 4; x++) {
                        result += bits[x + 4 * y + 16 * z] > 0 ? 'x' : '.';
                    }
                    result += System.lineSeparator();
                }
                result += System.lineSeparator();
            }

            return result;

        }
    }

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.