क्या यह पता लगाना संभव है कि निम्नलिखित समस्या में बहुपद में कोई अनुक्रम मौजूद है?


27

मैं एक समय के लिए निम्नलिखित समस्या के बारे में सोच रहा हूं, और मुझे इसके लिए एक बहुपद समाधान नहीं मिला है। केवल क्रूर-स्रोत। मैं एक NP- कम्पलीट प्रॉब्लम को कम करने की कोशिश कर रहा हूँ, वो भी बिना किसी सक्सेज के।

यहाँ समस्या है :


आपके पास सॉर्ट किए गए सेट सकारात्मक पूर्णांक जोड़े हैं। {(A1,B1),(A2,B2),,(An,Bn)}

(Ai,Bi)<(Aj,Bj)Ai<Aj(Ai=AjBi<Bj) (Ai,Bi)=(Aj,Bj)Ai=AjBi=Bj

निम्नलिखित ऑपरेशन एक जोड़ी के लिए लागू किया जा सकता है Swap(pair):। यह जोड़ी के तत्वों को स्वैप करता है, इसलिए बन जाएगा(10,50)(50,10)

जब सेट में एक जोड़ी बदली जाती है, तो सेट स्वचालित रूप से फिर से सॉर्ट हो जाता है (स्वैप की गई जोड़ी जगह से बाहर हो जाती है और यह सेट में अपने स्थान पर चली जाएगी)।

समस्या यह देखने पर होती है कि क्या कोई अनुक्रम है, जो किसी जोड़ी पर शुरू होता है, पूरे सेट को स्वैप करता है, निम्न स्थिति के साथ:

एक जोड़ी की अदला-बदली होने के बाद, अगले जोड़े की अदला-बदली या तो उत्तराधिकारी या सेट में पूर्ववर्ती जोड़ी से की जानी चाहिए।


इस समस्या का बहुपद समय समाधान या एनपी-पूर्ण समस्या को कम करना बहुत अच्छा होगा।

नोट:
यह पहले से ही एक निर्णय समस्या है। मैं जानना नहीं चाहता कि कौन सा अनुक्रम है: केवल यदि कोई अनुक्रम मौजूद है।

एक जोड़ी की अदला-बदली के बाद सेट को कैसे क्रमबद्ध किया जाता है, इसका उदाहरण

(6, 5)
(1,2)
(3,4)
(7,8)

अगर मैं पहली जोड़ी को स्वैप करता हूं, तो यह हो जाता है: , और सेट को छाँटने के बाद (सॉर्ट की गई जोड़ी को अपनी नई स्थिति में रखते हुए), हमारे पास है:(5,6)

(1,2)
(3,4)
(5,6)
(7,8)

फिर मुझे या तो (पूर्ववर्ती) जोड़ी या (सक्सेसर) स्वैप करना होगा, और प्रक्रिया को तब तक दोहराना होगा जब तक कि सभी जोड़े स्वैप न हो जाएं (यदि संभव हो)।( 7 , 8 )(3,4)(7,8)

महत्वपूर्ण:
आप पहले से स्वैप की गई जोड़ी को स्वैप नहीं कर सकते।
यदि 'स्वैप' ऑपरेशन का क्रम है, तो सभी जोड़े को एक बार और केवल एक बार नाम बदलना होगा।

उदाहरण जहां सभी जोड़े को स्वैप करना संभव नहीं है

( 1 , 4 ) ( 3 , 2 ) ( 5 , 5 )(0,0)
(1,4)
(3,2)
(5,5)


1
क्या फ़ाइल का नाम बदलने के बाद और आपके द्वारा नाम बदलने के बाद अगली फ़ाइल चुनने से पहले सूची को क्रमबद्ध किया गया है? क्या आप छँटाई स्थिति को फिर से लिख सकते हैं: iff ( ) या ( और ) या ( और और )? एक < एक ' एक = एक ' बी < बी ' एक = एक ' बी = बी ' सी < सी '(A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C
mjqxxxx

3
असाइनमेंट की समस्याओं का सामान्य तौर पर cstheory.stackexchange.com पर स्वागत नहीं है।
Tsuyoshi Ito

3
हम्म, मुझे यकीन नहीं है। आमतौर पर यहाँ तर्क यह है कि ठेठ होमवर्क के सवालों का जवाब देना एक अच्छा अभ्यास नहीं है क्योंकि ऐसा करने से भविष्य में किसी के लिए होमवर्क का उद्देश्य बर्बाद हो जाएगा। लेकिन इस मामले में, समस्या एक सामान्य समस्या की तरह नहीं दिखती है।
त्सुयोशी इतो

2
शायद अगर आप "यह एक होमवर्क था" से अलग एक प्रेरणा देते हैं, तो लोग दिलचस्पी ले सकते हैं और इसे बंद नहीं किया जाएगा। इसका एक संभावित अनुप्रयोग क्या हो सकता है?
मार्कोस विलग्रा

2
समस्या के सुधार के बारे में, आप फ़ाइलों के बारे में भूल सकते हैं और इसे इस तरह देख सकते हैं। आपके पास सकारात्मक पूर्णांक के जोड़े का एक सेट है , और नियम वही हैं जो आप इसे डालते हैं। प्रारंभ में पहले कॉलम में क्रमबद्ध किया जाता है, फिर आप बिंदुओं का नाम बदलना शुरू करते हैं। A={(x1,y1),,(xn,yn)}
मार्कोस विलगरा

जवाबों:


16

... मैंने एक एनपीसी समस्या से कमी का निर्माण करने के लिए कुछ पैटर्न खोजे, लेकिन "टोक" के साथ "प्रवाह" का प्रतिनिधित्व करने का तरीका नहीं खोजा ...

तो (कुछ काम के बाद) यह एक बहुपद एल्गोरिथ्म है ...

कलन विधि

शुरुआती सूची को लगातार " छेद " की एक सरणी के रूप में देखा जा सकता है । प्रत्येक प्रारंभिक जोड़ी , छेद संख्या पर " तत्व " । प्रत्येक जोड़ी की स्थिति से एक निर्देशित धार के रूप में देखी जा सकती है स्थिति के लिए । एक चाल में स्थिति पर एक तत्व और इसे अपने गंतव्य स्थान ले जाने में शामिल होता है (गंतव्य छेद एक खूंटी बन जाता है )। हम किनारे को हटाते हैं, और अगले कदम को चुनने के लिए आगे बढ़ते हैं जो दो निकटतम पहुंच योग्य तत्वों में से एक से शुरू होगाN2(aj,bj)bjajajbjbjajbjbk से स्थिति (केवल और बीच छेद की अनुमति है)। हमें लगातार चालों का क्रम ज्ञात करना चाहिए ।bjbjbkN

  • प्रत्येक के लिए पर विचार (सरणी की स्थिति में शुरू करने तत्व के रूप में) ।(aj,bj)bjajstart

    • प्रत्येक को अंतिम तत्व मानते हैं (स्थिति से स्थिति का छोर अंतिम छोर होगा)।(ak,bk),akajakendakbk

      • से चालों के एक दृश्य उत्पन्न निम्नलिखित मानदंड का उपयोग कर जब तक आप तत्व तक पहुंचने के (और एक समाधान नहीं मिला है), या एक को रोकने हालतstartend

जब आप एक चाल बनाते हैं तो आप स्थिति पर एक खूंटी को ठीक और सरणी को दो विभाजनों (बाएं) और (दाएं) में विभाजित किया जाता है और से (या से ) जाने का एकमात्र तरीका एक किनारे का उपयोग कर रहा है वह खूंटी के पार कूद गया। सेटbjLRLRRL

  • edgesLR = बाएं से दाएं किनारों की संख्या (अंतिम बढ़त गिनती नहीं है)
  • edgesRL = सही से छोड़ दिया करने के लिए किनारों की संख्या (अंतिम बढ़त गिनती नहीं है)
  • flow =edgesLRedgesRL

मामले:

ए) अगर तो दो विभाजनों में से एक अगम्य हो जाएगा, बंद करो|flow|>1

end>bjendR

flow=1Lend

flow=1end

flow=0Rend

end<bjendL

endRend(start,end)

हर कदम पर एक ही अनुनाद लागू करें।

जटिलता

प्रत्येक छेद पर प्रवाह O (N) में पहले से ही रखा जा सकता है और प्रत्येक स्कैन में पुन: उपयोग किया जा सकता है।

छोरों हैं:

for start = 1 to N
  for end = 1 to N
    for move = 1 to N
      make a move (fix a peg and update flows)
      check if another move can be done using flow     

गणना के दौरान कोई विकल्प नहीं दिया जाता है, इसलिए एल्गोरिथ्म की जटिलताO(N3)

कोड

यह एल्गोरिथ्म का एक कार्यशील जावा कार्यान्वयन है:

public class StrangeSort {
    static int PEG = 0xffffff, HOLE = 0x0;
    static int M = 0, N = 0, choices = 0, aux = 0, end;
    static int problem[][], moves[], edgeflow[], field[];    
    boolean is_hole(int x) { return x == HOLE; }
    boolean is_peg(int x) { return x == PEG; }
    boolean is_ele(int x) { return ! is_peg(x) && ! is_hole(x); };
    int []cp(int src[]) { // copy an array
        int res[] = new int[src.length];
        System.arraycopy(src, 0, res, 0, res.length);
        return res;
    }    
    /* find the first element on the left (dir=-1) right (dir=1) */
    int find(int pos, int dir, int nm) {
        pos += dir;
        while (pos >= 1 && pos <= M ) {
            int x = field[pos];
            if ( is_peg(x) || (pos == end && nm < N-1) ) return 0;
            if ( is_ele(x) ) return pos;
            pos += dir;
        }
        return 0;
    }
    void build_edges() {
        edgeflow = new int[M+1];
        for (int i = 1; i<=M; i++) {
            int start = i;
            int b = field[start];
            if (! is_ele(b)) continue;
            if (i == end) continue;
            int dir = (b > start)? 1 : -1;
            start += dir;
            while (start != b) { edgeflow[start] += dir; start += dir; }
        }
    }
    boolean rec_solve(int start, int nm) {
        boolean f;
        int j;
        int b = field[start];
        moves[nm++] = b;
        if (nm == N) return true;
        //System.out.println("Processing: " + start + "->" + field[start]);        
        field[start] = HOLE;
        field[b] = PEG;
        int dir = (b > start)? 1 : -1;
        int i = start + dir;
        while (i != b) { edgeflow[i] -= dir; i += dir; } // clear edge                
        int flow = edgeflow[b];
        if (Math.abs(flow) > 2) return false;
        if (end > b) {
            switch (flow) {
            case 1 :                    
                j = find(b,-1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            case -1 :
                return false;
            case 0 :          
                j = find(b,1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            }        
        } else {
            switch (flow) {
            case -1 :                    
                j = find(b,1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            case 1 :
                return false;
            case 0 :          
                j = find(b,-1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            }            
        }
        return false;
    }
    boolean solve(int demo[][]) {
        N = demo.length;
        for (int i = 0; i < N; i++)
            M = Math.max(M, Math.max(demo[i][0], demo[i][1]));
        moves = new int[N];
        edgeflow = new int[M+1];
        field = new int[M+1];
        problem = demo;        
        for (int i = 0; i < problem.length; i++) {
            int a = problem[i][0];
            int b = problem[i][1];
            if ( a < 1 || b < 1 || a > M || b > M || ! is_hole(field[a]) || ! is_hole(field[b])) {
                System.out.println("Bad input pair (" + a + "," + b + ")");
                return false;
            }
            field[a] = b;
        }
        for (int i = 1; i <= M; i++) {
            end = i;
            build_edges();
            if (!is_ele(field[i])) continue;
            for (int j = 1; j <= M; j++) {
                if (!is_ele(field[j])) continue;
                if (i==j) continue;
                int tmp_edgeflow[] = cp(edgeflow);
                int tmp_field[] = cp(field);
                choices = 0;
                //System.out.println("START: " + j + " " + " END: " + i);
                if (rec_solve(j, 0)) {
                    return true;
                }
                edgeflow = tmp_edgeflow;
                field = tmp_field;
            }
        }
        return false;
    }
    void init(int demo[][]) {

    }
    public static void main(String args[]) {
        /**** THE INPUT ********/        

        int demo[][] =  {{4,2},{5,7},{6,3},{10,12},{11,1},{13,8},{14,9}};

        /***********************/        
        String r = "";
        StrangeSort sorter = new StrangeSort();       
        if (sorter.solve(demo)) {
            for (int i = 0; i < N; i++) { // print it in clear text
                int b =  moves[i];
                for (int j = 0; j < demo.length; j++)
                    if (demo[j][1] == b)
                        r += ((i>0)? " -> " : "") + "(" + demo[j][0] + "," + demo[j][1] + ")";
            }             
            r = "SOLUTION: "+r;
        }
        else
            r = "NO SOLUTIONS";
        System.out.println(r);
    }    
}

यह एक दिलचस्प दृष्टिकोण है। सामान्य तौर पर, जब भी आप एक किनारे का उपयोग करते हैं, तो प्रत्येक दिशा में से पार किए गए अप्रयुक्त किनारों की संख्या के बराबर (या एक से भिन्न) होना चाहिए ; और यदि संख्याएं एक-दूसरे से भिन्न होती हैं, तो आप जानते हैं कि आपको कौन सा किनारा लेना चाहिए। जब संख्या समान होती है, तो आपके पास एक विकल्प होता है, जिसे आपको दोनों विकल्पों का परीक्षण करके हल करना होगा। यह एक कुशल पर्याप्त खोज रणनीति की तरह लगता है, लेकिन आप कैसे जानते हैं कि यह सबसे खराब स्थिति में बहुपद है? यानी, आपको कैसे पता चलेगा कि आप केवल विकल्पों का सामना करेंगे जहां प्रत्येक दिशा में अप्रयुक्त क्रॉसिंग किनारों की संख्या बराबर है? (a,b)bO(logn)
mjqxxxx

@mjqxxxx ... मैंने जावा एल्गोरिथ्म से मिलान करने के लिए पूरे उत्तर को फिर से लिखा ...
Marzio De Biasi

@mjqxxxx ... ठीक है, आखिरकार मुझे मिल गया ... :-)
Marzio De Biasi

2
यह मेरे लिए सही और बहुत सुंदर लग रहा है। एक बार जब आप एक किनारे , तो आप पार "पैदल" नहीं रह सकते हैं ; भर में केवल शेष संक्रमण अप्रयुक्त "छलांग" (निर्देशित किनारों) को पार कर रहे हैं, जो आप सभी का उपयोग करना चाहिए। यदि अंतिम बढ़त निर्दिष्ट की जाती है, तो आपको रूप में के एक ही तरफ हवा करने की आवश्यकता होती है । प्रत्येक किनारे के बाद चलने के लिए केवल एक ही संभव दिशा है, क्योंकि एक विषम (समान) संख्या में कूदना आपको विपरीत (उसी) पक्ष पर छोड़ देगा जहां आप शुरू में चले थे। इसलिए किनारों को शुरू करने और समाप्त करने के प्रत्येक विकल्प का परीक्षण बहुपद समय में किया जा सकता है। (a,b)bb(an,bn)ban
mjqxxxx

1
यह एक सुंदर एल्गोरिथ्म है। अंतिम चाल को ठीक करने के लिए मेरे साथ ऐसा कभी नहीं हुआ। छोटे बिंदु: (1) जैसा कि mjqxxxx ने लिखा है, अंत a_k होना चाहिए। अन्यथा हालत "अंत> b_j" गलत है। (2) या तो "प्रवाह" की परिभाषा को नकारना होगा, या बी और सी को स्वैप करना होगा।
त्सुयोशी इतो

10

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

, साथ असंतुष्ट निर्देशित किनारों सेट को देखते हुए , वहाँ एक आदेश इन किनारों का ऐसाn(a,b)a,b{1,2,,2n}(a1,b1),(a2,b2),...,(an,bn)

  • यदि के बीच है और , तो , औरajbiai+1ji
  • यदि के बीच है और , तो ?बी मैं एक मैं + 1 जे मैं + 1bjbiai+1ji+1

2
+1। समतुल्य समस्या को बताने का यह एक बहुत सरल तरीका है। बस एक स्पष्टीकरण: किनारों (ए, बी) को निर्देशित किया जाता है (इस अर्थ में कि किनारे (ए, बी) और किनारे (बी) के अलग-अलग अर्थ हैं)।
५३ पर त्सुयोशी इतो

@ त्सुयोशी: धन्यवाद; मैंने 'निर्देशित' कहने के लिए संपादन किया।
mjqxxxx

जैसा कि मैं समझता हूं कि वाक्यांश " और बीच " का अर्थ है । इसलिए मुझे लगता है कि यह पहले वाले द्वारा पूर्व अंकन को बदलने के लायक है। एक सी एक bacabc
ओलेकेंड्रा बोंडारेंको

@ ऑलेक्ज़ेंडर: यहाँ "b a और c के बीच है" का अर्थ है "या तो <b <c या c <b <a"।
Tsuyoshi Ito
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.