आइटमों को पुन: व्यवस्थित करते समय क्रमबद्ध कुंजियाँ बनाना


11

हमारे पास कई आइटम हैं जिन्हें अंतिम उपयोगकर्ता एक वांछित क्रम में व्यवस्थित करने में सक्षम होगा। आइटम का सेट अनियंत्रित है, लेकिन प्रत्येक आइटम में एक सॉर्ट कुंजी है जिसे संशोधित किया जा सकता है।

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

एक उदाहरण एल्गोरिथ्म में प्रत्येक प्रकार की कुंजी एक फ्लोटिंग पॉइंट नंबर होना चाहिए, और दो आइटमों के बीच एक आइटम रखने पर, औसत होने के लिए सॉर्ट कुंजी सेट करें। किसी आइटम को पहले या आखिरी में रखने से सबसे बाहरी मान + 1 प्राप्त होगा।

यहाँ समस्या यह है कि फ्लोटिंग पॉइंट प्रिसिजन फ़ेल होने का कारण हो सकता है। एक भिन्नात्मक संख्या का प्रतिनिधित्व करने के लिए दो पूर्णांक का उपयोग करना इसी तरह संख्या इतनी बड़ी हो सकती है कि वे नियमित रूप से संख्यात्मक प्रकारों में सटीक रूप से प्रतिनिधित्व नहीं कर सकते हैं (जैसे कि JSON के रूप में स्थानांतरित होने पर)। हम BigInts का उपयोग नहीं करना चाहेंगे।

क्या इसके लिए एक उपयुक्त एल्गोरिथ्म है जो उदाहरण के लिए, तार का उपयोग करके काम करेगा, जो इन कमियों से प्रभावित नहीं होगा?

हम बड़ी संख्या में चालों का समर्थन नहीं कर रहे हैं, लेकिन ऊपर वर्णित एल्गोरिथ्म लगभग 50 चालों के बाद एक डबल-सटीक फ़्लोटिंग पॉइंट नंबर पर विफल हो सकता है।


स्ट्रिंग्स एक स्पष्ट विकल्प हैं, क्योंकि आप बस उन्हें समाप्त करने के लिए पात्रों को जोड़कर रख सकते हैं। उस ने कहा, मुझे ऐसा लग रहा है कि इस से निपटने का एक बेहतर तरीका है।
रॉबर्ट हार्वे

अपने सिर के ऊपर से मैं यह नहीं देखता कि कैसे यह अन्य वस्तुओं की कुंजियों को संशोधित किए बिना तार का उपयोग करके काम करना है।
संपो

3
आपके द्वारा बताई जा रही समस्या
नाथन मेरिल

1
आप सूची में अन्य वस्तुओं को संशोधित नहीं करने से क्यों चिंतित हैं?
GER

1
आप इसे स्ट्रिंग्स के साथ कैसे काम करते हैं, यह इस प्रकार है: A, B, C- A, AA, B, C- A, AA, AB, B, C- A, AA, AAA, AAB, AAC, AB, AC, B, C। बेशक, आप शायद अपने पत्रों को अधिक स्थान देना चाहते हैं ताकि तार इतनी जल्दी न बढ़ें, लेकिन यह किया जा सकता है।
रॉबर्ट हार्वे

जवाबों:


4

सभी टिप्पणियों और उत्तरों के सारांश के रूप में:

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

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

एक सैद्धांतिक दृष्टिकोण से (यानी अनंत पुनरावर्तन की अनुमति देने वाला), एकमात्र समाधान जिसके बारे में मैं सोच सकता हूं, वह दो असीमित आकार के पूर्णांकों को भिन्नात्मक a / b के रूप में उपयोग कर रहा है। यह मध्य-आवेषण के लिए अनंत परिशुद्धता की अनुमति देता है, लेकिन संख्या तेजी से बड़ी हो सकती है।

स्ट्रिंग्स बड़ी संख्या में अपडेट का समर्थन करने में सक्षम हो सकती हैं (हालांकि मुझे अभी भी दोनों ऑपरेशनों के लिए एल्गोरिदम का पता लगाने में परेशानी हो रही है), लेकिन अनंत नहीं, क्योंकि आप पहली स्थिति में असीम रूप से कई जोड़ नहीं सकते हैं (कम से कम नियमित स्ट्रिंग प्रकार का उपयोग करके तुलना)।

इंटेगर्स को सॉर्ट कीज़ के लिए एक प्रारंभिक रिक्ति चुनने की आवश्यकता होगी, जो कि आपके द्वारा प्रदर्शन किए जाने वाले मध्य-आवेषण को सीमित कर सकती है। यदि आप शुरू में 1024 के अलावा की तरह की स्पेस देते हैं, तो आप आसन्न संख्याओं से पहले केवल 10 सबसे खराब मध्य-आवेषण प्रदर्शन कर सकते हैं। एक बड़ी प्रारंभिक रिक्ति का चयन करने से आप कितने पहले / अंतिम आवेषण का प्रदर्शन कर सकते हैं। 64-बिट पूर्णांक का उपयोग करना, आप किसी भी तरह से ~ 63 संचालन तक सीमित हैं, जिसे आपको मध्य-आवेषण और पहले / अंतिम आवेषण के बीच विभाजित करने की आवश्यकता है।

फ़्लोटिंग पॉइंट वैल्यूज़ का उपयोग करने से प्राथमिकता को चुनने की आवश्यकता दूर हो जाती है। एल्गोरिथ्म सरल है:

  1. डाला गया पहला तत्व एक सॉर्ट कुंजी है 0.0
  2. पहले या अंतिम में सम्मिलित एक तत्व (या स्थानांतरित) में पहले तत्व की सॉर्ट कुंजी है - क्रमशः 1.0 या अंतिम तत्व + 1.0।
  3. दो तत्व के बीच डाला गया एक तत्व (या स्थानांतरित) दोनों के औसत के बराबर एक सॉर्ट कुंजी है।

एक डबल-सटीक फ्लोट का उपयोग 52 सबसे खराब स्थिति वाले मध्य-आवेषण और प्रभावी रूप से अनंत (लगभग 1e15) पहले / अंतिम आवेषण की अनुमति देता है। व्यवहार में जब एल्गोरिदम के आसपास की वस्तुओं को स्वयं को स्वयं-सही करना चाहिए, क्योंकि हर बार जब आप किसी वस्तु को पहले या अंतिम रूप से स्थानांतरित करते हैं, तो यह उस सीमा का विस्तार करता है जिसका उपयोग किया जा सकता है।

डबल-सटीक फ़्लोट्स का भी लाभ है कि वे सभी प्लेटफार्मों द्वारा समर्थित हैं और आसानी से संग्रहीत और व्यावहारिक रूप से सभी परिवहन प्रारूपों और पुस्तकालयों द्वारा संग्रहीत किए जाते हैं। यह वही था जिसका हमने उपयोग किया था।


1

मैंने @ Sampo के सारांश के आधार पर टाइपस्क्रिप्ट में एक समाधान लिखा। कोड नीचे पाया जा सकता है।

रास्ते में कुछ अंतर्दृष्टि प्राप्त की।

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

  • प्रत्येक 1074: वें मध्य बिंदु विभाजन को हमें फ्लोटिंग पॉइंट रेंज को सामान्य करने की आवश्यकता होती है। हम यह पता लगाकर जांच करते हैं कि क्या नया मिडपॉइंट आक्रमणकारी को संतुष्ट करता है

    a.sortKey < m && m < b.sortKey

  • स्केलिंग से कोई फर्क नहीं पड़ता, जैसे-जैसे कीज़ को सामान्य किया जाता है, सामान्यीकरण अभी भी हर 1074मिडपॉइंट के विभाजन के साथ होता है । यदि हम संख्याओं को व्यापक रूप से शुरू करने के लिए वितरित करते हैं तो स्थिति में सुधार नहीं होगा।

  • सॉर्ट कुंजी सामान्यीकरण अविश्वसनीय रूप से दुर्लभ है। आप इस लागत को उस बिंदु पर परिशोधित करेंगे जहां सामान्यीकरण ध्यान देने योग्य नहीं है। हालाँकि, अगर आपके पास अधिक से अधिक तत्व हैं, तो मैं इस दृष्टिकोण से सावधान रहूँगा।


export interface HasSortKey {
  sortKey: number;
}

function normalizeList<T extends HasSortKey>(list: Array<T>) {
  const normalized = new Array<T>(list.length);
  for (let i = 0; i < list.length; i++) {
    normalized[i] = { ...list[i], sortKey: i };
  }
  return normalized;
}

function insertItem<T extends HasSortKey>(
  list: Array<T>,
  index: number,
  item: Partial<T>
): Array<T> {
  if (list.length === 0) {
    list.push({ ...item, sortKey: 0 } as T);
  } else {
    // list is non-empty

    if (index === 0) {
      list.splice(0, 0, { ...item, sortKey: list[0].sortKey - 1 } as T);
    } else if (index < list.length) {
      // midpoint, index is non-zero and less than length

      const a = list[index - 1];
      const b = list[index];

      const m = (a.sortKey + b.sortKey) / 2;

      if (!(a.sortKey < m && m < b.sortKey)) {
        return insertItem(normalizeList(list), index, item);
      }

      list.splice(index, 0, { ...item, sortKey: m } as T);
    } else if (index === list.length) {
      list.push({ ...item, sortKey: list[list.length - 1].sortKey + 1 } as T);
    }
  }
  return list;
}

export function main() {
  const normalized: Array<number> = [];

  let list: Array<{ n: number } & HasSortKey> = [];

  list = insertItem(list, 0, { n: 0 });

  for (let n = 1; n < 10 * 1000; n++) {
    const list2 = insertItem(list, 1, { n });
    if (list2 !== list) {
      normalized.push(n);
    }
    list = list2;
  }

  let m = normalized[0];

  console.log(
    normalized.slice(1).map(n => {
      const k = n - m;
      m = n;
      return k;
    })
  );
}

0

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


1
आपको हमेशा एक कुंजी नहीं मिल सकती है जो एक और स्ट्रिंग कुंजी से पहले है।
साम्पो

-1

पूर्णांक का उपयोग करें, और प्रारंभिक सूची के लिए सॉर्ट कुंजी को 500 * आइटम नंबर सेट करें। वस्तुओं के बीच सम्मिलित करते समय आप औसत का उपयोग कर सकते हैं। यह कई सम्मिलन को शुरू करने में सक्षम करेगा


2
यह वास्तव में फ्लोट का उपयोग करने से भी बदतर है। 500 का एक प्रारंभिक रिक्ति केवल 8-9 मिडपॉइंट सम्मिलन (2 ^ 9 = 512) की अनुमति देता है, जबकि एक डबल फ्लोट 50 के बारे में अनुमति देता है, जिसमें शुरू में रिक्ति का चयन करने का कोई मुद्दा नहीं है।
सांपो

500 गैप और फ्लोट्स का उपयोग करें !
रॉब मुल्डर

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