2016-12-22 2 views
7

में एक अवधि के अंतर और सीमाओं के सेट का पता लगाएं के लिए रास्ता मैं समय के एक नंबर मिल गया है रूबी में पर्वतमाला:कुशल रूबी

period = Time.parse('8:00am')..Time.parse('8:00pm') 
incidents = [ 
    Time.parse('7:00am')..Time.parse('9:00am'), 
    Time.parse('1:00pm')..Time.parse('3:00pm'), 
    Time.parse('1:30pm')..Time.parse('3:30pm'), 
    Time.parse('7:00pm')..Time.parse('9:00pm'), 
] 

मैं भीतर घटना मुक्त ब्लॉक की एक सरणी प्राप्त करने के लिए कोशिश कर रहा हूँ काल। कि ऊपर हो जाएगा के लिए:

[ 
    Time.parse('9:00am')..Time.parse('1:00pm') 
    Time.parse('3:30pm')..Time.parse('7:00pm') 
] 

से ऊपर यह घटनाओं ओवरलैप या अवधि के बाहर का विस्तार करने के लिए संभव है। क्या किसी भी परिचालन सीमा या कुछ ऐसा ही मौजूद है जो इस तरह की गणना को सरल बना देगा?

+0

यदि यह एक व्यावहारिक अनुप्रयोग है और आप PostgreSQL का उपयोग कर रहे हैं, तो आप [तारीख गणित कार्यक्षमता] (https://www.postgresql.org उपयोग करने पर विचार करना चाहिए /docs/9.1/static/functions-datetime.html) अपने एप्लिकेशन तर्क के अंदर संभालने के बजाय SQL क्वेरी के माध्यम से। – coreyward

+0

यदि आप इसे पूरी तरह से एल्गोरिदमिक विकास दृष्टिकोण से देख रहे हैं, [इस प्रश्न/उत्तर को आपको कब्ज के लिए कुछ चारा देना चाहिए] (http://stackoverflow.com/questions/1193477/fast-algorithm-to-quickly-find -इस दूरी एक नंबर अंतर्गत आता है करने के लिए में एक सेट-ऑफ-द पर्वतमाला? RQ = 1)। – coreyward

+0

@coreyward दुर्भाग्य से डेटा SQL डेटाबेस में संग्रहीत नहीं है। मैं वास्तव में स्पष्ट नहीं हूं कि लिंक पर आधारित एसक्यूएल संस्करण आसान होगा (अगर मैं पहले पीएसक्यूएल में आयात करना चाहता था)। – Stussa

उत्तर

2

Let एक सीमा हो full_range और ranges पर्वतमाला की एक सरणी, का प्रतिनिधित्व क्या प्रश्नकर्ता period और incidents क्रमश करार दिया हो। मैंने माना है कि सभी श्रेणियों में निहित तत्व किसी ऑब्जेक्ट्स हो सकते हैं, बशर्ते उन्हें तुलनात्मक वर्ग 'विधि <=> से तुलना की जा सके और यह कि मॉड्यूल Comparableinclude डी रहा है।

कोड

def uncovered_ranges(full_range, ranges) 
    ucrs = [full_range] 
    ranges.each do |r| 
    ucrs = ucrs.flat_map do |ucr| 
     if ucr.first >= r.last || ucr.last <= r.first 
     ucr 
     elsif r.first <= ucr.first && r.last >= ucr.last 
     nil 
     elsif r.first <= ucr.first && r.last < ucr.last 
     r.last..ucr.last 
     elsif r.first > ucr.first && r.last >= ucr.last 
     ucr.first..r.first 
     else 
     [ucr.first..r.first, r.last..ucr.last] 
     end 
    end.compact 
    end 
    ucrs 
end 

उदाहरण

full_range = 1..20 
ranges = [3..4, 6..8, 10..12, 8..14, 16..17, 20..20] 

uncovered_ranges(full_range, ranges) 
    #=> [1..3, 4..6, 14..16, 17..20] 

require 'time' 

full_range = Time.parse('8:00am')..Time.parse('8:00pm') 
    #=> 2016-12-22 08:00:00 -0800..2016-12-22 20:00:00 -0800 

ranges = [ 
    Time.parse('7:00am')..Time.parse('9:00am'), 
    Time.parse('1:00pm')..Time.parse('3:00pm'), 
    Time.parse('1:30pm')..Time.parse('3:30pm'), 
    Time.parse('7:00pm')..Time.parse('9:00pm'), 
] 
    #=> [2016-12-22 07:00:00 -0800..2016-12-22 09:00:00 -0800, 
    # 2016-12-22 13:00:00 -0800..2016-12-22 15:00:00 -0800, 
    # 2016-12-22 13:30:00 -0800..2016-12-22 15:30:00 -0800, 
    # 2016-12-22 19:00:00 -0800..2016-12-22 21:00:00 -0800] 

uncovered_ranges(full_range, ranges) 
    #=> [2016-12-22 09:00:00 -0800..2016-12-22 13:00:00 -0800, 
    # 2016-12-22 15:30:00 -0800..2016-12-22 19:00:00 -0800] 

स्पष्टीकरण

शायद मुझे समझाने के लिए क्या हो रहा है के लिए सबसे आसान और सबसे के माध्यम से जिस तरह से कुछ puts डालने के लिए हैबयान और ऊपर दिए गए पहले उदाहरण के लिए कोड चलाएं।

def uncovered_ranges(full_range, ranges) 
    ucrs = [full_range] 
    puts "ucrs initially=#{ucrs}" 
    ranges.each do |r| 
    puts "\ncovering range r=#{r}" 
    ucrs = ucrs.flat_map do |ucr| 
     puts " range uncovered so far ucr=#{ucr}" 
     if ucr.first >= r.last || ucr.last <= r.first 
     puts " in if #1, returning #{ucr}" 
     ucr 
     elsif r.first <= ucr.first && r.last >= ucr.last 
     puts " in if #2, returning nil" 
     nil 
     elsif r.first <= ucr.first && r.last < ucr.last 
     puts " in if #3, returning #{r.last..ucr.last}" 
     r.last..ucr.last 
     elsif r.first > ucr.first && r.last >= ucr.last 
     puts " in if #4, returning #{ucr.first..r.first}" 
     ucr.first..r.first 
     else 
     puts " in else, returning #{[ucr.first..r.first, r.last..ucr.last]}" 
     [ucr.first..r.first, r.last..ucr.last] 
     end 
    end.tap { |u| puts "ucrs after processing range #{r}=#{u}" }. 
     compact. 
     tap { |u| puts "ucrs after compact=#{u}" } 
    end 
    ucrs 
end 

uncovered_ranges 1..20, [3..4, 6..8, 10..12, 8..14, 16..17, 20..20] 

निम्नलिखित प्रिंट करता है।

ucrs initially=[1..20] 

covering range r=3..4 
    range uncovered so far ucr=1..20 
    in else, returning [1..3, 4..20] 
ucrs after processing range 3..4=[1..3, 4..20] 
ucrs after compact=[1..3, 4..20] 

covering range r=6..8 
    range uncovered so far ucr=1..3 
    in if #1, returning 1..3 
    range uncovered so far ucr=4..20 
    in else, returning [4..6, 8..20] 
ucrs after processing range 6..8=[1..3, 4..6, 8..20] 
ucrs after compact=[1..3, 4..6, 8..20] 

covering range r=10..12 
    range uncovered so far ucr=1..3 
    in if #1, returning 1..3 
    range uncovered so far ucr=4..6 
    in if #1, returning 4..6 
    range uncovered so far ucr=8..20 
    in else, returning [8..10, 12..20] 
ucrs after processing range 10..12=[1..3, 4..6, 8..10, 12..20] 
ucrs after compact=[1..3, 4..6, 8..10, 12..20] 

covering range r=8..14 
    range uncovered so far ucr=1..3 
    in if #1, returning 1..3 
    range uncovered so far ucr=4..6 
    in if #1, returning 4..6 
    range uncovered so far ucr=8..10 
    in if #2, returning nil 
    range uncovered so far ucr=12..20 
    in if #3, returning 14..20 
ucrs after processing range 8..14=[1..3, 4..6, nil, 14..20] 
ucrs after compact=[1..3, 4..6, 14..20] 

covering range r=16..17 
    range uncovered so far ucr=1..3 
    in if #1, returning 1..3 
    range uncovered so far ucr=4..6 
    in if #1, returning 4..6 
    range uncovered so far ucr=14..20 
    in else, returning [14..16, 17..20] 
ucrs after processing range 16..17=[1..3, 4..6, 14..16, 17..20] 
ucrs after compact=[1..3, 4..6, 14..16, 17..20] 

covering range r=20..20 
    range uncovered so far ucr=1..3 
    in if #1, returning 1..3 
    range uncovered so far ucr=4..6 
    in if #1, returning 4..6 
    range uncovered so far ucr=14..16 
    in if #1, returning 14..16 
    range uncovered so far ucr=17..20 
    in if #1, returning 17..20 
ucrs after processing range 20..20=[1..3, 4..6, 14..16, 17..20] 
ucrs after compact=[1..3, 4..6, 14..16, 17..20] 
    #=> [1..3, 4..6, 14..16, 17..20] 
3

संभव समाधान

इस range operator gem का उपयोग करना, इस स्क्रिप्ट (लगभग) वापसी होगी आप क्या चाहते हैं।

यह period से शुरू होता है, और दूसरे के बाद प्रत्येक incident को प्रतिस्थापित करता है।

के बाद से एक और से एक सीमा substracting दो श्रेणियों में परिणाम कर सकते हैं, स्क्रिप्ट वास्तव में [period] साथ शुरू होता है और नि: शुल्क घटना की एक सरणी पुनरावृत्तियों के बीच रहता रखता है:

require 'range_operators' 

incident_free = incidents.inject([period]) do |free_ranges, incident| 
    free_ranges.flat_map do |free_range| 
    free_range - incident 
    end 
end 

p incident_free 
#=> [2016-12-22 09:00:01 +0100..2016-12-22 12:59:59 +0100, 2016-12-22 15:30:01 +0100..2016-12-22 18:59:59 +0100] 

नोट्स

यह शिकायत Time#succ है कि अप्रचलित। आप या तो

class Time 
    def succ 
    self+1 
    end 
end 

प्रतिवाद चेतावनी दूर करने के लिए जोड़ सकते हैं या के साथ एक Gemfile उपयोग कर सकते हैं: Time के लिए एक ठीक से मणि के एक नए संस्करण स्थापित करने के लिए,

gem 'range_operators', :git => 'https://github.com/monocle/range_operators.git' 

स्क्रिप्ट 1 सेकंड के संकल्प के साथ काम करता है, और पहली श्रेणी 09:00:01 पर शुरू होती है क्योंकि 09:00:00 तक कोई घटना हुई थी।