एक बल्कि Knotty Conundrum


23

गाँठ की संरचना के आधार पर एक गाँठ के 2-डी आरेख को खींचने के लिए एक कार्यक्रम लिखें। एक गाँठ वास्तव में ऐसा लगता है जैसे: रस्सी का एक लूप जो बंधा हुआ है। गणित में, एक गाँठ आरेख से पता चलता है कि गाँठ बनाने के लिए रस्सी का एक टुकड़ा अपने आप पर या नीचे कहाँ से पार होता है। कुछ उदाहरण गाँठ चित्र नीचे दिखाए गए हैं:

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

उस पंक्ति में एक विराम होता है जहाँ रस्सी स्वयं से पार हो जाती है।

इनपुट: गाँठ का वर्णन करने वाला इनपुट पूर्णांक का एक सरणी है। एक गाँठ जहाँ रस्सी को n n से अधिक बार पार किया जाता है , को n पूर्णांकों की एक सरणी के रूप में दर्शाया जा सकता है , प्रत्येक का मान श्रेणी [0, n-1] में होता है। इस सरणी K को कॉल करते हैं

एक गाँठ का वर्णन करने वाले एरे को प्राप्त करने के लिए, प्रत्येक सेगमेंट को n-1 के माध्यम से संख्या दें। खंड 0 सेगमेंट 1 तक जाना चाहिए, जिससे सेगमेंट 2 तक जाना चाहिए, जिससे सेगमेंट 3 तक ले जाना चाहिए, और तब तक जब तक खंड n-1 लूप वापस नहीं हो जाता है और सेगमेंट की ओर जाता है 0. एक खंड समाप्त होता है जब रस्सी का एक और सेगमेंट उस पर पार हो जाता है ( आरेख में लाइन में एक ब्रेक द्वारा दर्शाया गया है)। आइए सरलतम गाँठ लें - ट्रेफ़िल गाँठ। जब हम सेग्मेंट्स की संख्या बढ़ाते हैं, तो खंड 2 समाप्त होता है जब खंड 2 उस पर पार हो जाता है; खंड 1 तब समाप्त होता है जब खंड 0 इसके पार हो जाता है; और खंड 2 तब समाप्त होता है जब खंड 1 उसके पार हो जाता है। इस प्रकार, गाँठ का वर्णन करने वाला सरणी [2, 0, 1] है। सामान्य तौर पर, सेगमेंट एक्स की शुरुआत होती है जहां सेगमेंट एक्स -1 मॉड एन छोड़ा जाता है, और जहां सेगमेंट के [एक्स] इसके ऊपर से पार होता है।

नीचे की छवि गाँठ आरेखों को दिखाती है, जिसमें लेबल वाले सेगमेंट और गाँठ का वर्णन करने वाली संगत सरणियाँ होती हैं।

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

शीर्ष तीन आरेख सच समुद्री मील हैं, जबकि नीचे तीन रस्सी के छोर हैं जो अपने आप को पार करते हैं लेकिन वास्तव में नॉटेड होते हैं (लेकिन जिनके पास अभी भी समान कोड हैं)।

आपका कार्य एक फ़ंक्शन लिखना है जो पूर्णांक K की एक सरणी लेता है (आप इसे कुछ अलग कह सकते हैं) जो एक गाँठ का वर्णन करता है (या रस्सी की लूप जो वास्तव में गाँठ नहीं है), और जो कि संबंधित गाँठ आरेख का उत्पादन करता है, जैसा कि ऊपर वर्णित है। उदाहरण। यदि आप कर सकते हैं, तो अपने कोड या स्पष्टीकरण के एक ungolfed संस्करण प्रदान करें, और अपने कोड के नमूना आउटपुट भी प्रदान करें। एक ही गाँठ का अक्सर कई अलग-अलग तरीकों से प्रतिनिधित्व किया जा सकता है, लेकिन अगर आपके फ़ंक्शन आउटपुट को संतुष्ट करता है गाँठ आरेख में इनपुट है, तो यह संभव प्रतिनिधित्वों में से एक है, आपका समाधान मान्य है।

यह कोड-गोल्फ है, इसलिए बाइट्स जीत में सबसे छोटा कोड है। रस्सी का प्रतिनिधित्व करने वाली रेखा में 1 पिक्सेल की मोटाई हो सकती है, हालांकि इसके नीचे और ओवर-क्रॉसिंग को स्पष्ट रूप से अलग-अलग होना चाहिए (रस्सी में ब्रेक का आकार दोनों तरफ कम से कम पिक्सेल की रस्सी से मोटाई से अधिक होना चाहिए) ।

मैं उन उत्तरों को अपवाह करूंगा जो अंतर्निहित गाँठ सिद्धांत क्षमताओं पर भरोसा करते हैं, लेकिन अंत में जो चुना जाता है वह अंतर्निहित गाँठ सिद्धांत क्षमताओं पर भरोसा नहीं कर सकता है।

मेरे संकेतन के बारे में मैं जो कुछ भी जानता हूं: मेरा मानना ​​है कि ऐसे मूल्य हैं, जो किसी भी गाँठ या अननोट के अनुरूप नहीं लगते हैं। उदाहरण के लिए, अनुक्रम [2, 3, 4, 0, 1] को आकर्षित करना असंभव प्रतीत होता है।

उस के अलावा, मान लीजिए कि आप एक क्रॉसिंग लेते हैं और उस क्रॉसिंग से शुरू करते हुए, एक दिशा में रस्सी के मार्ग का अनुसरण करते हैं और प्रत्येक अनलम्बे क्रॉसिंग को लेबल करते हैं जो आप सक्सेसफुल अधिक से अधिक अभिन्न मूल्यों के साथ आते हैं। बारी-बारी से गांठों के लिए, मेरे अंकन को इस तरह के लेबलिंग में बदलने के लिए एक सरल एल्गोरिथ्म है, और बारी-बारी से गांठों के लिए इस लेबलिंग को गॉस कोड में परिवर्तित करना तुच्छ है:

template<size_t n> array<int, 2*n> LabelAlternatingKnot(array<int, n> end_at)
{
    array<int, n> end_of;
    for(int i=0;i<n;++i) end_of[end_at[i]] = i;
    array<int, 2*n> p;
    for(int& i : p) i = -1;
    int unique = 0;
    for(int i=0;i<n;i++)
    {
        if(p[2*i] < 0)
        {
            p[2*i] = unique;
            p[2*end_of[i] + 1] = unique;
            ++unique; 
        }
        if(p[2*i+1] < 0)
        {
            p[2*i+1] = unique;
            p[2*end_at[i]] = unique;
            ++unique;
        }
    }
    return p;
}
template<size_t n> auto GetGaussCode(array<int, n> end_at)
{
    auto crossings = LabelAlternatingKnot(end_at);
    for(int& i : crossings) ++i;
    for(int i=1;i<2*n;i+=2) crossings[i] = -crossings[i];
    return crossings;
}

4
आपको शायद ऐसा करने के लिए बिल्डरों पर प्रतिबंध लगाना चाहिए। (इस बिंदु पर, मुझे आश्चर्य होगा कि अगर गणितज्ञ के पास एक नहीं है।)

7
@ ais523 अब मैं KnotDataगणितज्ञ में उपयोग नहीं कर सकता ...: '(
जंगवान मिन

1
मैं नोटेशन क्रॉसिंग आरेख के लिए आपके द्वारा उपयोग किए जाने वाले अंकन के बारे में उत्सुक हूं। इसका कोई नाम है? क्या दो गैर-समतुल्य गांठों के लिए एक ही सरणी होना संभव है?
xnor

2
@ ais523: मैथमैटिक पूरी तरह से एक बिलिन है Knot! उदाहरण उपयोग: << Units`; Convert[Knot, Mile/Hour]पैदावार 1.1507794480235425 Mile/Hour। (मुझे लगता है कि यह सच है या गलत है इसकी परवाह किए बिना मजाकिया है; लेकिन यह वास्तव में सच है।)
ग्रेग मार्टिन

1
[०], [०,१], [०,१,२], [१०२] और कई अन्य सरणियों में "नॉट्स" का उत्पादन होता है जो कि अनकोट के बराबर होता है, हालांकि एक साधारण लूप का उत्पादन गलत होगा। उन मामलों में से कोई भी। संकेतन [0] का अर्थ विशेष रूप से रस्सी का एक लूप होता है जो अपने आप को एक बार पूरी तरह से प्रतिच्छेद करता है, और यह बताना बहुत आसान है कि स्क्रीन पर खींची गई आकृति इनपुट संकेतन को संतुष्ट करती है या नहीं।
जे। एंटोनियो पेरेज़

जवाबों:


22

कोड पेज 850 में GNU प्रोलॉग, 622 634 668 बाइट्स

अद्यतन : कार्यक्रम का पिछला संस्करण कभी-कभी क्रॉसिंग को इतना तंग कर रहा था कि वे ठीक से प्रस्तुत नहीं करेंगे, जो कल्पना का उल्लंघन करता है। मैंने इसे रोकने के लिए कुछ अतिरिक्त कोड में जोड़ा है।

अद्यतन : जाहिरा तौर पर पीपीसीजी नियमों को कार्यक्रम से बाहर निकलने और राज्य को बहाल करने के लिए अतिरिक्त कोड की आवश्यकता होती है, जैसा कि यह शुरुआत में था। यह कार्यक्रम को कुछ लंबा बनाता है और इसमें कोई एल्गोरिथम ब्याज नहीं जोड़ता है, लेकिन नियम अनुपालन के हितों में, मैंने इसे बदल दिया है।

गोल्फ का कार्यक्रम

GNU प्रोलॉग का उपयोग करना क्योंकि इसमें एक बाधा सॉल्वर सिंटैक्स है जो पोर्टेबल प्रोलॉग के अंकगणितीय सिंटैक्स से थोड़ा कम है, कुछ बाइट्स को बचाता है।

y(A,G):-A=1;A= -1;A=G;A is-G.
z(A/B,B,G):-y(A,G),y(B,G),A=\= -B.
v(D,E,G):-E=1,member(_-_,D),p(D);F#=E-1,nth(F,D,M),(M=[_];M=L-S/R,z(L,O,G),J is F+O,nth(J,D,I/U-T/Q),(I=O,Q#=R-1,S=T;K is J+O,R=0,n(S-T-V),y(U,G),U\=O,U=\= -O,I=U,nth(K,D,O/_-V/_))),v(D,F,G).
i([H|K],S):-K=[]->assertz(n(S-H-0));T#=S+1,assertz(n(S-H-T)),i(K,T).
t([],1,_):-!.
t(D,R,G):-S#=R-1,t(E,S,G),H#=G-1,length(F,H),append(F,[[x]|E],D).
s(1,2).
s(-1,1).
s(S,T):-S>1->T=3;T=0.
r(I/O-_,C):-s(I,J),s(O,P),N#=J*4+P+1,nth(N,"│┐┌?└─?┌┘?─┐?┘└│",C).
r([X],C):-X\=y->C=10;C=32.
p([]).
p([H|T]):-r(H,C),put_code(C),!,p(T).
g(4).
g(G):-g(H),G#=H+1.
m(K):-i(K,0),g(G),t(D,G,G),length(D,L),v(D,L,G),abolish(n/1).

कलन विधि

यह उन समस्याओं में से एक है जहां यह जानना मुश्किल है कि कैसे शुरू करें। यह स्पष्ट नहीं है कि दिए गए अंकन से गाँठ के आकार को कैसे काम किया जाए, क्योंकि यह आपको यह बताने नहीं देता है कि आप किसी भी स्थान पर बाईं या दाईं ओर झुकना चाहते हैं (और जैसे कि, संकेतन अस्पष्ट हो सकता है)। मेरा समाधान पुराने गोल्फिंग स्टैंडबाय का उपयोग करने के लिए प्रभावी ढंग से था: एक अविश्वसनीय रूप से अक्षम कार्यक्रम लिखें जो सभी संभावित आउटपुट उत्पन्न करता है और फिर देखें कि क्या वे इनपुट से मेल खाते हैं। (यहाँ इस्तेमाल किया एल्गोरिथ्म थोड़ा और अधिक कुशल है, क्योंकि प्रोलोग कभी-कभी मृत अंत निकाल सकता है, लेकिन कम्प्यूटेशनल जटिलता में सुधार करने के लिए इसके पास पर्याप्त जानकारी नहीं है।)

आउटपुट टर्मिनल आर्ट के माध्यम से है। GNU प्रोलॉग ऐसा लगता है कि ASCII के अनुरूप एक एकल बाइट वर्ण सेट चाहता है, लेकिन इसकी परवाह नहीं करता है कि किसका उपयोग किया जाता है (क्योंकि यह उच्च बिट सेट के साथ अक्षरों को अपारदर्शी के रूप में मानता है)। नतीजतन, मैंने आईबीएम 850 का उपयोग किया, जो व्यापक रूप से समर्थित है और इसमें हमारे लिए आवश्यक रेखा चित्र हैं।

उत्पादन

कार्यक्रम उनके बाउंडिंग बॉक्स के आकार के क्रम में, सभी संभव गाँठ छवियों को खोजता है, फिर जब यह पहली बार बाहर निकलता है। यहाँ क्या उत्पादन के लिए जैसा दिखता है m([0]).:

 ┌┐
┌│┘
└┘ 

मेरे कंप्यूटर पर चलने में 290.528 सेकंड का समय लगा; कार्यक्रम बहुत कुशल नहीं है। मैंने इसे दो घंटे तक चलाना छोड़ दिया m([0,1]), और इसके साथ समाप्त हुआ:

┌┐┌┐
└─│┘
 └┘ 

टिप्पणियों के साथ अनप्लग्ड संस्करण

स्टैक एक्सचेंज के वाक्यविन्यास हाइलाइटर में प्रोलॉग के लिए गलत टिप्पणी का प्रतीक है, इसलिए %टिप्पणियों के बजाय (जो कि वास्तव में प्रोलॉग का उपयोग करता है), यह स्पष्टीकरण % #टिप्पणियों का उपयोग करता है (जो कि शुरू होने के कारण समान हैं %, लेकिन सही ढंग से हाइलाइट करें)।

% # Representation of the drawing is: a list of:
% #     indelta/outdelta-segment/distance  (on path)
% # and [x] or [_]                         (off path; [x] for border).
% # A drawing is valid, and describes a knot, if the following apply
% # (where: d[X] is the Xth element of the drawing,
% #         k[S] is the Sth element of the input,
% #         n[S] is S + 1 modulo the number of sections):
% # d[X]=_/O-S-R, R>1 implies d[X+O]=O/_-S-(R-1)
% # d[X]=_/O-S-0 implies d[X+O]=_/_-k[S]-_
% #                  and d[X+O*2]=O/_-n[S]-_
% # all outdeltas are valid deltas (±1 row/column)

% # not technically necessary, but makes it possible to compile the
% # code (and thus makes the program faster to test):
:- dynamic([n/1]).

% # legal delta combinations:
y(A,G):-A=1;A= -1;              % # legal deltas are 1, -1,
        A=G;A is-G.             % # grid size, minus grid size
z(A/B,B,G):-y(A,G),y(B,G),      % # delta components are valid
            A=\= -B.            % # doesn't U-turn
% # z returns the outdelta for convenience (= byte savings) later on

% # We use v (verify) to verify the first E-1 elements of a drawing D.
% # nth is 1-indexed, so we recurse from length(D)+1 down to 2, with
% # 1 being the trivial base case. After verifying, the grid is printed.
% # This version of the program causes v to exit with success after
% # printing one grid (and uses p to do the work of deciding when that is).
v(D,E,G):-E=1,                  % # base case:
          member(_-_,D),        % # ensure the grid is nonempty
          p(D);                 % # print the grid (and exit)

                                % # otherwise, recursive case:
          F#=E-1,nth(F,D,M),    % # check the last unchecked element
          (
            M=[_];              % # either it's not on the path; or
            M=L-S/R,            % # it's structured correctly, and
            z(L,O,G),           % # it has a valid delta;
            J is F+O,           % # find the outdelta'd element index
            nth(J,D,I/U-T/Q),   % # and the outdelta'd element
            (
              I=O,Q#=R-1,S=T;   % # if not an endpoint, points to next pixel
              K is J+O,         % # else find segment beyond the path:
              R=0,              % # it's an endpoint,
              n(S-T-V),         % # it points to the correct segment,
              y(U,G),           % # ensure we can do NOT comparisons on U
              U\=O,U=\= -O,     % # the line we jump is at right angles
              I=U,              % # the line we jump is straight
              nth(K,D,O/_-V/_)  % # the pixel beyond has a correct indelta,
                                % # and it has the correct segment number
            )
          ),
          v(D,F,G).             % # recurse

% # We use i (init) to set up the k, n tables (k and n are fixed for
% # any run of the program, at least). S is the number of elements that
% # have been removed from K so far (initially 0). To save on characters,
% # we combine k and n into a single predicate n.
i([H|K],S):-K=[]->             % # if this is the last element,
            assertz(n(S-H-0)); % # section 0 comes after S;
            T#=S+1,            % # otherwise, add 1 to S,
            assertz(n(S-H-T)), % # that section comes after S,
            i(K,T).            % # and recurse.

% # We use t (template) to create a template drawing. First argument is
% # the drawing, second argument is the number of rows it has plus 1,
% # third argument is the number of columns it has plus 1.
t([],1,_):-!.
t(D,R,G):-S#=R-1,t(E,S,G),      % # recurse,
          H#=G-1,length(F,H),   % # F is most of this row of the grid
          append(F,[[x]|E],D).  % # form the grid with F + border + E

% # We use s (shrink) to map a coordinate into a value in the range 0, 1, 2, 3.
s(1,2).
s(-1,1).
s(S,T):-S>1->T=3;T=0.
% # We use r (representation) to map a grid cell to a character.
r(I/O-_,C):-s(I,J),s(O,P),N#=J*4+P+1,nth(N,"│┐┌?└─?┌┘?─┐?┘└│",C).
r([X],C):-X\=y->C=10;C=32.
% # We use p (print) to pretty-print a grid.
% # The base case allows us to exit after printing one knot.
p([]).
p([H|T]):-r(H,C),put_code(C),!,p(T).

% # We use g (gridsize) to generate grid sizes.
g(4).
g(G):-g(H),G#=H+1.

% # Main program.
m(K):-i(K,0),                  % # initialize n
      g(G),                    % # try all grid sizes
      t(D,G,G),                % # generate a square knot template, size G
      length(D,L),             % # find its length
      v(D,L,G),                % # verify and print one knot
      % # Technically, this doesn't verify the last element of L, but we know
      % # it's a border/newline, and thus can't be incorrect.
      abolish(n/1).            % # reset n for next run; required by PPCG rules

मैंने GNU प्रोलॉग डाउनलोड किया, अपने कोड को एक .txt फ़ाइल में कॉपी किया, इसे ascii-encoded .pl फ़ाइल के रूप में सहेजा, और कंसोल में m ([0])
J. एंटोनियो पेरेज़

क्या कोई ऐसा तरीका है जिससे आप कार्यक्रम को अधिक कुशल बना सकते हैं?
जे। एंटोनियो पेरेज़

मुझे संदेह है कि कार्यक्रम को और अधिक कुशल बनाया जा सकता है, लेकिन इसका कोई स्पष्ट या आसान तरीका नहीं है। बाएं-से-दाएं / ऊपर-नीचे के बजाय, गाँठ के साथ-साथ चलने के लिए मूल्यांकन क्रम को बदलने से संभवतः मदद मिलेगी, लेकिन मुझे यकीन नहीं है कि यह कितना मदद करेगा (और यह काफी अधिक कोड भी होगा) इस प्रकार एक कोड-गोल्फ में व्यवहार्य नहीं )।

आप समझते हैं कि मैं अनिच्छुक क्यों हूं? मेरा मतलब है ... यहां तक ​​कि सबसे अच्छे कोड का परीक्षण करने की आवश्यकता है, और आपका समाधान इतना जटिल है कि मैं यह भी सत्यापित नहीं कर सकता कि यह बहुत सरल गाँठ ([2, 0, 1] गाँठ) को पुन: पेश करेगा।
जे। एंटोनियो पेरेज़

मैं समझ गया, हां। हालांकि, यह कोड-गोल्फ में निहित की तरह है , खासकर जब यह प्रोलॉग की बात आती है। कोड वास्तव में बहुत सरल है, यही कारण है कि यह इतनी धीमी गति से चलता है;एक जटिल समस्या पर कोड-गोल्फ हमेशा एक जानवर-बल समाधान की ओर जाता है जो विनिर्देश के खिलाफ सभी संभावित आउटपुट की जांच करता है। कार्यक्रम को अधिक कुशल बनाने के लिए कुछ करने से यह काफी हद तक लंबा हो जाएगा, और संभवतः इसे समझने और सही साबित करने के लिए भी कठिन हो जाएगा।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.