अतिव्यापी सीमाओं के समतल के लिए एल्गोरिथ्म


16

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

हालांकि, रेंज न केवल पूर्णांक हैं, और मैं एक सभ्य एल्गोरिथ्म की तलाश कर रहा हूं जिसे जावास्क्रिप्ट या पायथन, आदि में आसानी से लागू किया जा सकता है।

उदाहरण डेटा: उदाहरण डेटा

उदाहरण समाधान: यहाँ छवि विवरण दर्ज करें

माफी अगर यह एक डुप्लिकेट है, लेकिन मैं अभी तक एक समाधान खोजने के लिए कर रहा हूँ।


आप यह कैसे निर्धारित करते हैं कि हरा नीला के ऊपर है, लेकिन पीले और नारंगी रंग के नीचे है? क्या रंग रेंज क्रम में लागू होते हैं? यदि ऐसा है, तो एल्गोरिथ्म स्पष्ट लगता है; बस ... erm, क्रम में रंग पर्वतमाला लागू करें।
रॉबर्ट हार्वे

1
हाँ, वे क्रम में लागू होते हैं। लेकिन यह समस्या है - आप श्रेणियों को 'लागू' कैसे करेंगे?
जॉलीवेट

1
क्या आप अक्सर रंग जोड़ते / हटाते हैं, या क्या आपको क्वेरी गति के लिए अनुकूलन करने की आवश्यकता है? आपके पास आमतौर पर कितने "पर्वतमाला" होंगे? 3? 3000?
तेलस्टिन

बहुत बार रंगों को जोड़ना / हटाना नहीं होगा, और 10-20 श्रेणियों के बीच कहीं भी होगा, 4+ अंकों की सटीकता के साथ। इसीलिए सेट विधि काफी उपयुक्त नहीं है, क्योंकि सेटों को 1000+ आइटम लंबा होना होगा। जिस विधि से मैं गया हूं वह वही है जिसे मैंने पायथन में पोस्ट किया है।
जॉलीवेट

जवाबों:


10

आप किस रंग में हैं, इसका ट्रैक रखने के लिए एक स्टैक का उपयोग करके बाएं से दाएं चलो। असतत मानचित्र के बजाय, ब्रेक-पॉइंट के रूप में अपने डेटासेट में 10 नंबर का उपयोग करें।

एक खाली स्टैक के साथ शुरू करना, और start0 पर सेट करना, लूप जब तक हम अंत तक नहीं पहुंचते:

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

यह एक मानसिक रन-थ्रू आपका उदाहरण डेटा दिया गया है:

# Initial data.
flattened = []
stack = []
start = 0
# Stack is empty.  Look for the next starting point at 0 or later: "b", 0 - Push it and all lower levels onto stack
flattened = [ (b, 0, ?) ]
stack = [ r, b ]
start = 0
# End of "b" is 5.4, next higher-colored start is "g" at 2 - Delimit and continue
flattened = [ (b, 0, 2), (g, 2, ?) ]
stack = [ r, b, g ]
start = 2
# End of "g" is 12, next higher-colored start is "y" at 3.5 - Delimit and continue
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, ?) ]
stack = [ r, b, g, y ]
start = 3.5
# End of "y" is 6.7, next higher-colored start is "o" at 6.7 - Delimit and continue
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, 6.7), (o, 6.7, ?) ]
stack = [ r, b, g, y, o ]
start = 6.7
# End of "o" is 10, and there is nothing starting at 12 or later in a higher color.  Next off stack, "y", has already ended.  Next off stack, "g", has not ended.  Delimit and continue.
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, 6.7), (o, 6.7, 10), (g, 10, ?) ]
stack = [ r, b, g ]
start = 10
# End of "g" is 12, there is nothing starting at 12 or later in a higher color.  Next off stack, "b", is out of range (already ended).  Next off stack, "r", is out of range (not started).  Mark end of current color:
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, 6.7), (o, 6.7, 10), (g, 10, 12) ]
stack = []
start = 12
# Stack is empty.  Look for the next starting point at 12 or later: "r", 12.5 - Push onto stack
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, 6.7), (o, 6.7, 10), (g, 10, 12), (r, 12.5, ?) ]
stack = [ r ]
start = 12
# End of "r" is 13.8, and there is nothing starting at 12 or higher in a higher color.  Mark end and pop off stack.
flattened = [ (b, 0, 2), (g, 2, 3.5), (y, 3.5, 6.7), (o, 6.7, 10), (g, 10, 12), (r, 12.5, 13.8) ]
stack = []
start = 13.8
# Stack is empty and nothing is past 13.8 - We're done.

"स्टैक पर इसके रास्ते में कुछ और" से आपका क्या मतलब है?
Guillaume07

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

एक और विचार मुझे समझ में नहीं आता है, कृपया, आप पहले यह बताएं कि "यदि स्टैक खाली है: पहले रंग को शुरू या शुरू होने से पहले देखें," तो कोड नमूने में आप टिप्पणी करते हैं "# स्टैक खाली है। अगले के लिए देखें। प्रारंभिक बिंदु 0 या बाद में "। तो एक बार यह पहले और एक बार बाद में है
Guillaume07

1
@ Guillaume07 हां, एक टाइपो, सही संस्करण दो बार कोड ब्लॉक में है (दूसरा नीचे की ओर टिप्पणी है कि "स्टैक खाली है।") शुरू होता है। मैंने उस बुलेट पॉइंट को एडिट किया है।
इज़्काता

3

यह समाधान सबसे सरल लगता है। (या कम से कम, सबसे आसान समझ)

जरूरत है कि दो रेंज घटाना करने के लिए एक समारोह है। दूसरे शब्दों में, ऐसा कुछ जो यह देगा:

A ------               A     ------           A    ----
B    -------    and    B ------        and    B ---------
=       ----           = ----                 = ---    --

जो काफी सरल है। तब आप बस प्रत्येक श्रेणी के माध्यम से पुनरावृति कर सकते हैं, सबसे कम से शुरू कर सकते हैं , और प्रत्येक के लिए, इसके ऊपर से सभी श्रेणियों को घटा सकते हैं, बदले में। आखिर तुमने इसे हासिल कर ही लिया है।


यहाँ पायथन में रेंज सबट्रैक्टर का कार्यान्वयन है:

def subtractRanges((As, Ae), (Bs, Be)):
    '''SUBTRACTS A FROM B'''
    # e.g, A =    ------
    #      B =  -----------
    # result =  --      ---
    # Returns list of new range(s)

    if As > Be or Bs > Ae: # All of B visible
        return [[Bs, Be]]
    result = []
    if As > Bs: # Beginning of B visible
        result.append([Bs, As])
    if Ae < Be: # End of B visible
        result.append([Ae, Be])
    return result

इस फ़ंक्शन का उपयोग करते हुए, इस तरह से किया जा सकता है: (A 'स्पैन' का अर्थ है एक सीमा, जैसा कि 'श्रेणी' पायथन कीवर्ड है)

spans = [["red", [12.5, 13.8]],
["blue", [0.0, 5.4]],
["green", [2.0, 12.0]],
["yellow", [3.5, 6.7]],
["orange", [6.7, 10.0]]]

i = 0 # Start at lowest span
while i < len(spans):
    for superior in spans[i+1:]: # Iterate through all spans above
        result = subtractRanges(superior[1], spans[i][1])
        if not result:      # If span is completely covered
            del spans[i]    # Remove it from list
            i -= 1          # Compensate for list shifting
            break           # Skip to next span
        else:   # If there is at least one resulting span
            spans[i][1] = result[0]
            if len(result) > 1: # If there are two resulting spans
                # Insert another span with the same name
                spans.insert(i+1, [spans[i][0], result[1]])
    i += 1

print spans

यह देता है [['red', [12.5, 13.8]], ['blue', [0.0, 2.0]], ['green', [2.0, 3.5]], ['green', [10.0, 12.0]], ['yellow', [3.5, 6.7]], ['orange', [6.7, 10.0]]], जो सही है।


अंत में आपका आउटपुट प्रश्न में अपेक्षित आउटपुट से मेल नहीं खाता है ...
इज़्काटा

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

2

यदि डेटा वास्तव में आपके नमूना डेटा के दायरे के समान है, तो आप इस तरह से एक नक्शा बना सकते हैं:

map = [0 .. 150]

for each color:
    for loc range start * 10 to range finish * 10:
        map[loc] = color

फिर पर्वतमाला उत्पन्न करने के लिए बस इस नक्शे के माध्यम से चलें

curcolor = none
for loc in map:
    if map[loc] != curcolor:
        if curcolor:
            rangeend = loc / 10
        make new range
        rangecolor = map[loc]
        rangestart = loc / 10

काम करने के लिए, मूल्यों को आपके नमूना डेटा की तुलना में अपेक्षाकृत कम सीमा में होना चाहिए।

संपादित करें: सच्ची फ़्लोट्स के साथ काम करने के लिए, एक उच्च स्तरीय मानचित्रण उत्पन्न करने के लिए मानचित्र का उपयोग करें और फिर सीमाओं को बनाने के लिए मूल डेटा का संदर्भ लें।

map = [0 .. 15]

for each color:
   for loc round(range start) to round(range finish):
        map[loc] = color

curcolor = none
for loc in map
    if map[loc] != curcolor:

        make new range
        if loc = round(range[map[loc]].start)  
             rangestart = range[map[loc]].start
        else
             rangestart = previous rangeend
        rangecolor = map[loc]
        if curcolor:
             if map[loc] == none:
                 last rangeend = range[map[loc]].end
             else
                 last rangeend = rangestart
        curcolor = rangecolor

यह एक बहुत अच्छा समाधान है, मैं इससे पहले आया हूं। हालांकि, मैं एक और अधिक सामान्य समाधान की तलाश कर रहा हूं जो किसी भी मनमाना फ्लोट रेंज का प्रबंधन कर सकता है ... (यह 563.807 - 770.100 जैसी किसी चीज़ के लिए सबसे अच्छा नहीं होगा)
जॉलीवेट

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

2

यहाँ स्काला में अपेक्षाकृत सरल समाधान है। किसी अन्य भाषा में पोर्ट करना बहुत मुश्किल नहीं होना चाहिए।

case class Range(name: String, left: Double, right: Double) {
  def overlapsLeft(other: Range) =
    other.left < left && left < other.right

  def overlapsRight(other: Range) =
    other.left < right && right < other.right

  def overlapsCompletely(other: Range) =
    left <= other.left && right >= other.right

  def splitLeft(other: Range) = 
    Range(other.name, other.left, left)

  def splitRight(other: Range) = 
    Range(other.name, right, other.right)
}

def apply(ranges: Set[Range], newRange: Range) = {
  val left     = ranges.filter(newRange.overlapsLeft)
  val right    = ranges.filter(newRange.overlapsRight)
  val overlaps = ranges.filter(newRange.overlapsCompletely)

  val leftSplit  =  left.map(newRange.splitLeft)
  val rightSplit = right.map(newRange.splitRight)

  ranges -- left -- right -- overlaps ++ leftSplit ++ rightSplit + newRange
}

val ranges = Vector(
  Range("red",   12.5, 13.8),
  Range("blue",   0.0,  5.4),
  Range("green",  2.0, 12.0),
  Range("yellow", 3.5,  6.7),
  Range("orange", 6.7, 10.0))

val flattened = ranges.foldLeft(Set.empty[Range])(apply)
val sorted = flattened.toSeq.sortBy(_.left)
sorted foreach println

applySetपहले से लागू की गई सभी श्रेणियों में से एक को ले लेता है, ओवरलैप पाता है, फिर एक नया सेट घटाता है जो ओवरलैप होता है और साथ ही नई रेंज और नई स्प्लिट रेंज देता है। प्रत्येक इनपुट रेंज के साथ foldLeftबार-बार कॉल apply


0

बस शुरू से क्रमबद्ध श्रेणियों का एक सेट रखें। वह श्रेणी जोड़ें जो सब कुछ कवर करती है (-oo .. + oo)। श्रेणी r जोड़ने के लिए:

let pre = last range that starts before r starts

let post = earliest range that starts before r ends

now iterate from pre to post: split ranges that overlap, remove ranges that are covered, then add r
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.