किसी सरणी को इंटरलेक्ट करने के लिए इन-प्लेस एल्गोरिथ्म


62

आपको तत्वों की एक सरणी दी गई है2n

a1,a2,,an,b1,b2,bn

कार्य सरणी को इंटरलेक्ट करना है, इन-प्लेस एल्गोरिथ्म का उपयोग करना जैसे कि परिणामी सरणी जैसा दिखता है

b1,a1,b2,a2,,bn,an

यदि इन-प्लेस आवश्यकता नहीं थी, तो हम आसानी से एक नया एरे और कॉपी एलिमेंट्स बना सकते हैं, जो एक टाइम एल्गोरिथ्म दे रहा है।O(n)

इन-प्लेस आवश्यकता के साथ, एक डिवाइड और विजेता एल्गोरिथ्म एल्गोरिथ्म को ।θ(nlogn)

तो सवाल यह है:

क्या कोई टाइम एल्गोरिथ्म है, जो इन-प्लेस भी है?O(n)

(नोट: आप एक समान लागत WORD RAM मॉडल मान सकते हैं, इसलिए इन-प्लेस का अनुवाद स्थान प्रतिबंध के लिए किया जाता है।O(1)


1
यह स्टैकओवरफ्लो पर है लेकिन वे एक गुणवत्ता समाधान नहीं देते हैं। शीर्ष-रेटेड उत्तर है: "यह समस्या उतनी तुच्छ नहीं है जितना लोग करते हैं। होमवर्क? LOL। arXiv पर एक समाधान है " लेकिन arxiv समाधान को कुछ संख्या सिद्धांत + अन्य कागजात में सबूतों को संदर्भित करने की आवश्यकता होती है। यहाँ पर सक्सेस सॉल्यूशन रखना अच्छा होगा।
जो


स्टैक ओवरफ्लो पर एक और धागा: stackoverflow.com/questions/15996288/…
Nayuki

जवाबों:


43

यहाँ जो जो के द्वारा लिंक पेपर से एल्गोरिथ्म पर विस्तृत जवाब है: http://arxiv.org/abs/0805.1598

पहले आइए हम एक एल्गोरिथ्म पर विचार करें जो डिवाइड और जीत का उपयोग करता है।Θ(nlogn)

1) फूट डालो और जीतो

हम दे रहे हैं

a1,a2,,b1,b2,bn

अब विभाजित करने और जीतने के लिए, कुछ , हम सरणी प्राप्त करने का प्रयास करते हैंm=Θ(n)

[a1,a2,,am,b1,b2,,bm],[am+1,,an,bm+1,bn]

और फिर से आना।

ध्यान दें कि भाग चक्रीय पारी है

b1,b2,bm,am+1,an

am+1,an,b1,bm

स्थानों द्वारा ।m

यह एक क्लासिक है और इसे तीन रिवर्सल और इन- समय द्वारा किया जा सकता है ।O(n)

इस प्रकार विभाजित और जीतना आपको समान पुनरावृत्ति के साथ एक एल्गोरिथ्म देता है ।Θ(nlogn)T(n)=2T(n/2)+Θ(n)

2) क्रमचय चक्र

अब, समस्या का एक और तरीका है विच्छेद चक्र के एक सेट के रूप में क्रमचय पर विचार करें।

क्रमपरिवर्तन द्वारा दिया जाता है ( से शुरू होता है )1

j2jmod2n+1

यदि हम किसी तरह से जानते थे कि चक्र क्या थे, निरंतर अतिरिक्त स्थान का उपयोग करके, हम एक तत्व को चुनकर क्रमचय का एहसास कर सकते हैं , तो निर्धारित करें कि वह तत्व कहां जाता है (उपरोक्त सूत्र का उपयोग करके), लक्ष्य स्थान में तत्व को अस्थायी स्थान में डालें, डाल दें उस लक्ष्य में तत्व और चक्र के साथ जारी है। एक बार जब हम एक चक्र के साथ हो जाते हैं तो हम अगले चक्र के एक तत्व पर चले जाते हैं और उस चक्र का पालन करते हैं।AA

यह हमें एक टाइम अल्गोरिदम देगा, लेकिन यह मानता है कि हम "किसी तरह जानते थे कि सटीक चक्र क्या थे" और इस बुक-कीपिंग को स्पेस भीतर करने की कोशिश कर रहे हैं इस समस्या को कठिन बनाता है।O(n)O(1)

यह वह जगह है जहाँ कागज संख्या सिद्धांत का उपयोग करता है।

यह दिखाया जा सकता है कि, जब , स्थिति में , तत्व भिन्न चक्रों में होते हैं और हर चक्र में एक तत्व होता है स्थिति ।2n+1=3k13,32,,3k13m,m0

यह इस तथ्य का उपयोग करता है कि का एक जनरेटर है ।2(Z/3k)

इस प्रकार जब , चक्र दृष्टिकोण का पालन करता है, तो हमें एक समय एल्गोरिथ्म मिलता है, प्रत्येक चक्र के लिए, हम जानते हैं कि वास्तव में कहां से शुरू करें: शक्तियां ( सहित ) स्थान में गणना की जा सकती है ।2n+1=3kO(n)31O(1)

3) अंतिम एल्गोरिथम

अब हम उपरोक्त दो को जोड़ते हैं: विभाजित और जीतना + क्रमचय चक्र।

हम एक विभाजन करते हैं और जीतते हैं, लेकिन उठाते हैं ताकि शक्ति और ।m2m+13m=Θ(n)

तो बजाय दोनों "आधा" पर recursing पर, हम केवल एक पर recurse और कर अतिरिक्त काम।Θ(n)

यह हमें पुनरावृत्ति (कुछ ) देता है और इस प्रकार हमें एक समय, अंतरिक्ष एल्गोरिथ्म!T(n)=T(cn)+Θ(n)0<c<1O(n)O(1)


4
वह खूबसूरत है।
राफेल

1
बहुत अच्छा। क्रमपरिवर्तन उदाहरणों के माध्यम से जाना, मैं अब इसे सबसे अधिक समझता हूं। दो सवाल: 1. आप वास्तव में मूल्य मी कैसे पाते हैं? कागज का दावा है कि यह ओ (लॉग एन) लेता है, क्यों? 2. क्या एक समान दृष्टिकोण का उपयोग करके किसी सरणी को DE-interleave करना संभव है?
संख्यात्मक 3

2
@ num3ric: 1) आप की उच्चतम शक्ति पाते हैं जो कि । तो यह । 2)। हां, यह संभव है, मेरा मानना ​​है कि मैंने स्टैकओवरफ्लो पर कहीं उत्तर जोड़ा था। उस मामले में साइकिल नेताओं का मानना ​​है कि ( = शक्ति ) के लिए निकला था । 3<nO(logn)2a3b2m+13
आर्यभट्ट

@ आर्यभट्ट हम केवल दो "पड़ाव" के बजाय एक "आधे" पर ही पुनर्विचार क्यों करते हैं?
चीनिनिटी

1
@ आर्यभट्ट क्या इस एल्गोरिथ्म को दो सरणियों से अधिक इंटरलेव करने के लिए विस्तारित किया जा सकता है? उदाहरण के लिए को या ऐसा ही कुछ। a1,a2,,an,b1,b2,,bn,c1,c2,,cnc1,b1,a1,c2,b2,a2,,cn,bn,an
डबल

18

मुझे पूरा यकीन है कि मुझे एक एल्गोरिथ्म मिला है जो संख्या सिद्धांत या चक्र सिद्धांत पर निर्भर नहीं है। ध्यान दें कि बाहर काम करने के लिए कुछ विवरण हैं (संभवतः कल), लेकिन मुझे पूरा विश्वास है कि वे बाहर काम करेंगे। मैं हाथ लगा रहा हूं क्योंकि मैं सोने जा रहा हूं, इसलिए नहीं कि मैं समस्याओं को छिपाने की कोशिश कर रहा हूं :)

चलो Aपहली सरणी, हो Bदूसरा, |A| = |B| = Nऔर मान लेते हैं N=2^kकुछ के लिए k, सादगी के लिए। आज्ञा देना , समावेशी के माध्यम से सूचकांकों A[i..j]के Aसाथ , का समावेश हो। एरे 0-आधारित हैं। आज्ञा देना (0-आधारित) दायीं बिट की स्थिति यह है कि की है '1' वापसी , सही से गिनती। यह एल्गोरिथ्म इस प्रकार काम करता है।ijRightmostBitPos(i)i

GetIndex(i) {
    int rightPos = RightmostBitPos(i) + 1;
    return i >> rightPos;
}

Interleave(A, B, N) {
    if (n == 1) {
        swap(a[0], b[0]);
    }
    else {
        for (i = 0; i < N; i++)
            swap(A[i], B[GetIndex(i+1)]);

        for (i = 1; i <= N/2; i*=2)
            Interleave(B[0..i/2-1], B[i/2..i-1], i/2);

        Interleave(B[0..N/2], B[N/2+1..N], n/2);
    }
}

चलो 16 संख्याओं की एक सरणी लेते हैं, और चलो बस स्वैप का उपयोग करके उन्हें इंटरले करना शुरू करते हैं, और देखते हैं कि क्या होता है:

1 2 3 4 5 6 7 8    | 9 10 11 12 13 14 15 16
9 2 3 4 5 6 7 8    | 1 10 11 12 13 14 15 16
9 1 3 4 5 6 7 8    | 2 10 11 12 13 14 15 16
9 1 10 4 5 6 7 8   | 2 3 11 12 13 14 15 16
9 1 10 2 5 6 7 8   | 4 3 11 12 13 14 15 16
9 1 10 2 11 6 7 8  | 4 3 5 12 13 14 15 16
9 1 10 2 11 3 7 8  | 4 6 5 12 13 14 15 16
9 1 10 2 11 3 12 8 | 4 6 5 7 13 14 15 16
9 1 10 2 11 3 12 4 | 8 6 5 7 13 14 15 16

विशेष रूप से ब्याज दूसरे सरणी का पहला भाग है:

|
| 1
| 2
| 2 3
| 4 3
| 4 3 5
| 4 6 5
| 4 6 5 7
| 8 6 5 7

पैटर्न स्पष्ट होना चाहिए: हम वैकल्पिक रूप से एक नंबर को अंत तक जोड़ते हैं और सबसे कम संख्या को एक उच्च संख्या से बदल देते हैं। ध्यान दें कि हम हमेशा एक संख्या जोड़ते हैं जो हमारे पास पहले से मौजूद उच्चतम संख्या से अधिक है। अगर हम किसी भी तरह यह पता लगाने में सक्षम थे कि किसी भी समय कौन सी संख्या सबसे कम है, तो हम ऐसा आसानी से कर सकते हैं।

अब, हम बड़े उदाहरणों के लिए देखते हैं कि क्या हम एक पैटर्न देख सकते हैं। ध्यान दें कि उपरोक्त उदाहरण के निर्माण के लिए हमें सरणी के आकार को ठीक करने की आवश्यकता नहीं है। कुछ बिंदु पर, हमें यह विन्यास मिलता है (दूसरी पंक्ति 16 को हर संख्या से घटाती है):

16 24 20 28 18 22 26 30 17 19 21 23 25 27 29 31
0   8  4 12  2  6 10 14  1  3  5  7  9 11 13 15

अब यह स्पष्ट रूप से एक पैटर्न दिखाता है: "1 3 5 7 9 11 13 15" सभी 2 अलग हैं, "2 6 10 14" सभी 4 अलग हैं और "4 12" 8 अलग हैं। इसलिए हम एक एल्गोरिथ्म तैयार कर सकते हैं जो हमें बताता है कि अगली सबसे छोटी संख्या क्या होगी: तंत्र बहुत अधिक बाइनरी नंबर कैसे काम करता है। आपके पास सरणी के अंतिम आधे के लिए थोड़ा सा है, दूसरी तिमाही के लिए थोड़ा सा है, और इसी तरह।

यदि हमें इन बिट्स को संग्रहीत करने के लिए पर्याप्त स्थान की अनुमति है (हमें बिट्स की आवश्यकता है, लेकिन हमारे कम्प्यूटेशनल मॉडल यह अनुमति देता है - सरणी में एक सूचक को भी बिट्स की आवश्यकता है ), हम यह पता लगा सकते हैं कि में स्वैप करने के लिए कौन सी संख्या है समय परिशोधन।लॉग एन हे ( 1 )lognlognO(1)

इसलिए हम सरणी के पहले आधे हिस्से को समय और स्वैप में इसकी इंटरलेस्ड अवस्था में प्राप्त कर सकते हैं । हालांकि, हमें अपनी सरणी के दूसरे भाग को ठीक करना होगा, जो सभी गड़बड़ लगता है ("8 6 5 7 13 14 15 16")।O ( n )O(n)O(n)

अब, अगर हम इस दूसरे भाग के पहले भाग को 'सॉर्ट' कर सकते हैं, तो हम "5 6 7 8 13 13 14 15 16" के साथ समाप्त करते हैं, और इस आधे हिस्से को पुनरावृत्ति करते हुए इस चाल को करेंगे: हम सरणी को समय ( पुनरावर्ती कॉल जिनमें से प्रत्येक इनपुट आकार को आधा करता है)। ध्यान दें कि हमें स्टैक की आवश्यकता नहीं है क्योंकि ये कॉल पुनरावर्ती हैं, इसलिए हमारा स्थान उपयोग रहता है ।O ( लॉग एन ) O ( 1 )O(n)O(logn)O(1)

अब, सवाल यह है कि क्या उस हिस्से में कुछ पैटर्न है जिसे हमें क्रमबद्ध करने की आवश्यकता है? 32 संख्याओं की कोशिश करने से हमें "16 12 10 14 9 11 13 15" को ठीक करने के लिए मिलता है। ध्यान दें कि हमारे यहाँ एक ही पैटर्न है! "९ ११ १३ १५", "१० १४" और "१२" एक ही अंदाज में एक साथ समूहीकृत हुए हैं जिन्हें हमने पहले देखा था।

अब, चाल इन सबपार्ट्स को पुनरावृत्ति करने के लिए है। हम "16" और "12" से "12 16" तक इंटरलेव करते हैं। हम "12 16" और "10 14" से "10 12 14 16" तक इंटरलेव करते हैं। हम "10 12 14 16" और "9 11 13 15" से "9 10 11 12 13 14 15 16" तक इंटरलेव करते हैं। यह पहले भाग को क्रमबद्ध करता है।

ऊपर की तरह, इस ऑपरेशन की कुल लागत । इन सभी को जोड़ते हुए, हम अभी भी का कुल रनिंग टाइम प्राप्त करने का प्रबंधन करते हैं ।O ( n )O(n)O(n)

एक उदाहरण:

Interleave the first half:
1 2 3 4 5 6 7 8    | 9 10 11 12 13 14 15 16
9 2 3 4 5 6 7 8    | 1 10 11 12 13 14 15 16
9 1 3 4 5 6 7 8    | 2 10 11 12 13 14 15 16
9 1 10 4 5 6 7 8   | 2 3 11 12 13 14 15 16
9 1 10 2 5 6 7 8   | 4 3 11 12 13 14 15 16
9 1 10 2 11 6 7 8  | 4 3 5 12 13 14 15 16
9 1 10 2 11 3 7 8  | 4 6 5 12 13 14 15 16
9 1 10 2 11 3 12 8 | 4 6 5 7 13 14 15 16
9 1 10 2 11 3 12 4 | 8 6 5 7 13 14 15 16
Sort out the first part of the second array (recursion not explicit):
8 6 5 7 13 14 15 16
6 8 5 7 13 14 15 16
5 8 6 7 13 14 15 16
5 6 8 7 13 14 15 16
5 6 7 8 13 14 15 16
Interleave again:
5 6 7 8   | 13 14 15 16
13 6 7 8  | 5 14 15 16
13 5 7 8  | 6 14 15 16
13 5 14 8 | 6 7 15 16
13 5 14 6 | 8 7 15 16
Sort out the first part of the second array:
8 7 15 16
7 8 15 16
Interleave again:
7 8 | 15 16
15 8 | 7 16
15 7 | 8 16
Interleave again:
8 16
16 8
Merge all the above:
9 1 10 2 11 3 12 4 | 13 5 14 6 | 15 7 | 16 8

दिलचस्प। क्या आप एक औपचारिक प्रमाण लिखने और लिखने के लिए तैयार होंगे? मुझे पता है कि एक और एल्गोरिथ्म है (जो पाया पेपर में संदर्भित) जो बिट्स के साथ व्यवहार करता है। शायद आपने इसे फिर से खोज लिया है!
आर्यभट्ट

1

यहाँ एक गैर-पुनरावर्ती में रेखीय समय एल्गोरिथ्म में एक अतिरिक्त भंडारण के साथ किसी सरणी के दो हिस्सों को इंटरलेव करने के लिए है।

सामान्य विचार सरल है: सरणी के पहले आधे भाग से बाएं से दाएं चलें, सही मानों को जगह में स्वैप करना। जैसा कि आप आगे बढ़ते हैं, अभी तक उपयोग किए जाने वाले बाएं मानों को सही मानों द्वारा खाली किए गए स्थान में स्वैप किया जाता है। केवल चाल पता लगा रही है कि उन्हें फिर से कैसे निकालना है।

हम आकार एन की एक सरणी के साथ शुरू करते हैं 2 लगभग बराबर हिस्सों में विभाजित।
[ left_items | right_items ]
जैसे ही हम इसे प्रोसेस करते हैं, यह बन जाता है
[ placed_items | remaining_left_items| swapped_left_items | remaining_right_items]

स्वैप स्पेस निम्न पैटर्न के साथ बढ़ता है: ए) आसन्न दाएं आइटम को हटाकर और बाईं ओर से एक नए आइटम में स्वैप करके अंतरिक्ष बढ़ाएं; ख) बाईं ओर से एक नई वस्तु के साथ सबसे पुरानी वस्तु को स्वैप करें। यदि बाईं वस्तुओं की संख्या 1..N है, तो यह पैटर्न दिखता है

step swapspace index changed
1    A: 1         0
2    B: 2         0
3    A: 2 3       1
4    B: 4 3       0     
5    A: 4 3 5     2
6    B: 4 6 5     1
7    A: 4 6 5 7   3
...

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

यह वह सारी जानकारी है जो हमें अनुक्रम के 1 आधे हिस्से को रैखिक समय में आबाद करने की आवश्यकता है।

जब हम मिडपॉइंट पर पहुंचते हैं, तो सरणी के तीन भाग होंगे: [ placed_items | swapped_left_items | remaining_right_items] यदि हम स्वैप किए गए आइटम को अनसक्रैबल कर सकते हैं, तो हमने समस्या को आधे आकार में कम कर दिया है, और दोहरा सकते हैं।

स्वैप स्थान को हटाने के लिए, हम निम्नलिखित संपत्ति का उपयोग करते हैं: Nवैकल्पिक परिशिष्ट और swap_oldest संचालन द्वारा निर्मित एक अनुक्रम में वे N/2आइटम होंगे जहां उनकी उम्र द्वारा दी गई है A025480(N/2)..A025480(N-1)(पूर्णांक विभाजन, छोटे मान पुराने हैं)।

उदाहरण के लिए, यदि बाएं आधे हिस्से में मूल रूप से 1..19 मान है, तो स्वैप स्पेस में होगा [16, 12, 10, 14, 18, 11, 13, 15, 17, 19]। A025480 (9..18) [2, 5, 1, 6, 3, 7, 0, 8, 4, 9], जो वास्तव में सबसे पुराने से नवीनतम वस्तुओं के अनुक्रमित की सूची है।

इसलिए हम इसके माध्यम से आगे बढ़ सकते हैं और इसके S[i]साथ अदला-बदली कर सकते हैं S[ A(N/2 + i)]। यह भी रैखिक समय है।

शेष जटिलता यह है कि आखिरकार आप एक ऐसी स्थिति में पहुंच जाएंगे जहां सही मूल्य कम सूचकांक पर होना चाहिए, लेकिन यह पहले से ही बाहर हो गया है। नया स्थान ढूंढना आसान है: आइटम की अदला-बदली करने के लिए बस इंडेक्स गणना फिर से करें। जब तक आप किसी अनवस्थित स्थान का पता नहीं लगाते हैं, तब तक कुछ चरणों में श्रृंखला का पालन करना आवश्यक हो सकता है।

इस बिंदु पर, हमने आधे सरणी को मर्ज कर दिया है, और दूसरे हिस्से में अनरेटेड हिस्सों के क्रम को बनाए रखा है, बिल्कुल N/2 + N/4स्वैप के साथ । हम बाकी सरणी के माध्यम से कुल N + N/4 + N/8 + ....स्वैप के लिए जारी रख सकते हैं जो कि सख्ती से कम है 3N/2

A025480 की गणना कैसे करें:
इसे OEIS में परिभाषित किया गया है क्योंकि a(2n) = n, a(2n+1) = a(n).एक वैकल्पिक सूत्रीकरण है a(n) = isEven(n)? n/2 : a((n-1)/2)। यह बिटवाइज़ ऑपरेशंस का उपयोग करके एक सरल एल्गोरिथ्म की ओर जाता है:

index_t a025480(index_t n){
    while (n&1) n=n>>1;
    return n>>1;  
}

यह एन के लिए सभी संभावित मूल्यों पर एक परिशोधित ओ (1) ऑपरेशन है (1/2 की आवश्यकता 1 शिफ्ट, 1/4 को 2, 1/8 की आवश्यकता 3, ...) । एक और भी तेज़ विधि है जो कम से कम महत्वपूर्ण शून्य बिट की स्थिति का पता लगाने के लिए एक छोटी लुकअप तालिका का उपयोग करती है।

यह देखते हुए कि, यहाँ C में एक कार्यान्वयन है:

static inline index_t larger_half(index_t sz) {return sz - (sz / 2); }
static inline bool is_even(index_t i) { return ((i & 1) ^ 1); }

index_t unshuffle_item(index_t j, index_t sz)
{
  index_t i = j;
  do {
    i = a025480(sz / 2 + i);
  }
  while (i < j);
  return i;
}

void interleave(value_t a[], index_t n_items)
{
  index_t i = 0;
  index_t midpt = larger_half(n_items);
  while (i < n_items - 1) {

    //for out-shuffle, the left item is at an even index
    if (is_even(i)) { i++; }
    index_t base = i;

    //emplace left half.
    for (; i < midpt; i++) {
      index_t j = a025480(i - base);
      SWAP(a + i, a + midpt + j);
    }

    //unscramble swapped items
    index_t swap_ct  = larger_half(i - base);
    for (index_t j = 0; j + 1 < swap_ct ; j++) {
      index_t k = unshuffle_item(j, i - base);
      if (j != k) {
        SWAP(a + midpt + j, a + midpt + k);
      }
    }
    midpt += swap_ct;
  }
}

यह एक काफी कैश-फ्रेंडली एल्गोरिथम होना चाहिए, क्योंकि 3 में से 2 डेटा स्थानों को क्रमिक रूप से एक्सेस किया जाता है और संसाधित होने वाले डेटा की मात्रा सख्ती से कम हो रही है। लूप की शुरुआत में परीक्षण की उपेक्षा करके इस पद्धति को एक फेरबदल से एक फेरबदल में बदल दिया जा सकता है is_even

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