ওভারল্যাপিং ব্যাপ্তিগুলি সমতল করার জন্য অ্যালগরিদম


16

আমি সম্ভাব্য-ওভারল্যাপিং সংখ্যার রেঞ্জগুলির একটি তালিকা সমতলকরণ (বিভাজন) এর একটি ভাল উপায় খুঁজছি। সমস্যা এই প্রশ্নের সাথে খুব মিলে যায়: ওভারল্যাপিং তারিখের সীমা বিভক্ত করার দ্রুততম উপায় এবং আরও অনেকগুলি।

তবে, ব্যাপ্তিগুলি কেবলমাত্র পূর্ণসংখ্যা নয়, এবং আমি একটি শালীন অ্যালগরিদম খুঁজছি যা জাভাস্ক্রিপ্ট বা পাইথন ইত্যাদিতে সহজেই প্রয়োগ করা যেতে পারে etc.

উদাহরণ ডেটা: উদাহরণ ডেটা

উদাহরণ সমাধান: এখানে চিত্র বর্ণনা লিখুন

যদি এটি সদৃশ হয় তবে দুঃখিত, তবে আমি এখনও এর সমাধান খুঁজে পাইনি।


আপনি কীভাবে নির্ধারণ করতে পারেন যে সবুজ নীলের উপরে, তবে হলুদ এবং কমলার নীচে? রঙ ব্যাপ্তি ক্রম প্রয়োগ করা হয়? যদি এটি হয় তবে অ্যালগরিদমটি সুস্পষ্ট বলে মনে হচ্ছে; ঠিক ... এরম, রঙের ব্যাপ্তিগুলি যথাযথভাবে প্রয়োগ করুন।
রবার্ট হার্ভে

1
হ্যাঁ, তারা যথাযথভাবে প্রয়োগ করা হয়। তবে সমস্যাটি হ'ল আপনি কীভাবে ব্যাপ্তিগুলি প্রয়োগ করতে পারবেন?
জলিওয়াত

1
আপনি কি প্রায়শই রঙগুলি যুক্ত / সরান, বা আপনার ক্যোয়ারী গতির জন্য অনুকূলিতকরণ প্রয়োজন? আপনার সাধারণত কতগুলি "রেঞ্জ" থাকবে? 3? 3000?
টেলাস্টিন

খুব ঘন ঘন রং যুক্ত / মুছে ফেলা হবে না এবং 4+ ডিজিট যথার্থতার সাথে 10-20 রেঞ্জের মধ্যে কোথাও থাকবে। সে কারণেই সেট পদ্ধতিটি বেশ উপযুক্ত নয়, কারণ সেটগুলি 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
@ গুইলিউম07 বর্তমান এবং নির্বাচিত পরবর্তী সূচনার মধ্যে যে কোনও পদক্ষেপ নেই। নমুনা তথ্য এটি দেখায় না, তবে ভাবুন যে হলুদ সবুজ রঙের আগে শুরু হয়েছিল - আপনি সবুজ এবং হলুদ উভয় স্ট্যাকের দিকে ঠেলাতে হবে যাতে হলুদ শেষ হলে, সবুজটির শেষে এখনও স্ট্যাকের ঠিক জায়গায় থাকে সুতরাং এটি এখনও চূড়ান্ত
ফলাফলটিতে

আরেকটি ধারণা যা আমি বুঝতে পারি না, দয়া করে, কেন আপনি প্রথমে বলছেন "যদি স্ট্যাকটি খালি থাকে: শুরু করার আগে বা তার আগে শুরু হওয়া প্রথম রঙটির সন্ধান করুন", তারপরে কোডের নমুনায় আপনি মন্তব্য করেছেন "# স্ট্যাকটি ফাঁকা। পরবর্তীটির জন্য দেখুন 0 বা তার পরে প্রারম্ভিক বিন্দু "। তাই একবার এটি আগে ও একবার এটা পরে
Guillaume07

1
@ গুইলিউম ০7 হ্যাঁ, একটি টাইপো, সঠিক সংস্করণটি কোড ব্লকে দু'বার রয়েছে (দ্বিতীয়টি নীচে কাছাকাছি থাকা মন্তব্য যা "স্ট্যাকটি খালি রয়েছে" "শুরু হয়)। আমি বুলেট পয়েন্ট সম্পাদনা করেছি।
ইজকাটা 16'19

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

এই ফাংশনটি ব্যবহার করে, বিশ্রামগুলি এইভাবে করা যেতে পারে: (এ 'স্প্যান' অর্থ একটি ব্যাপ্তি, কারণ 'পরিসর' পাইথন কীওয়ার্ড)

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)। একটি ব্যাপ্তি আর যুক্ত করতে:

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.