【问题标题】:How can i find overlapping times in python我如何在python中找到重叠时间
【发布时间】:2015-05-20 10:47:08
【问题描述】:

我尝试了很久,一直没能找到python中如何分割重叠时间的逻辑。

这是我的测试用例时间

  1. 上午 9 点 - 下午 5 点
  2. 8am-10am、12pm-1pm、4pm-6pm(多个条目)

案例 1:

Entry 1: 9am - 5pm
Test Entry: 10am - 12pm
Output: 9am - 10am,  10am - 12pm, 12pm - 5pm

现在我希望如果输入重叠时间,那么系统应该将它分成不同的时间。喜欢

entry1.start_time = 9:00am
entry1.end_time = 5:00pm

现在如果我输入这个条目

entry2.start_time = 10:00am
entry2.end_time = 12:00pm

那么系统应该把它拆分成

9am - 10am, 10am - 12pm, 12pm - 5pm

案例 2

Entry 1 - 9am - 5pm
Test Entry    8am - 10am
Then output should be   8am - 10am ,   10am - 5pm

案例 3 这是案例1的修改

Entry 1 - 9am - 10am

Test Entry 8am - 11am

Then output should be   8am - 11am

我被逻辑困住了。这是我开始的一段代码

class TimeSplitter(object):
    def __init__(self, **kwargs):
        self.entries = kwargs.pop('entries', [])
        self.test_entry = kwargs.pop('testEntry', '')

    def test_overlap(self, entry1, test_entry):
        if test_entry.start_time > entry1.start_time and  test_entry.start_time < entry1.end_time
        pass

我不能再继续下去了

【问题讨论】:

  • 您可以使用 24 H 时间格式,然后可以轻松比较值。
  • 在您的 案例 1 中,entry2entry1 的范围内,因此我们将其分成三个。但是,在您的情况 3 中,同样的事情重复了,entry1 完全在entry2 的范围内,但我们只得到一个输出?这里有什么交易?
  • @MarkusMeskanen 案例 3 与案例 1 相反。案例 3 条目 1 在 2 内,但案例 1 条目 2 在第一个条目内。

标签: python algorithm


【解决方案1】:

预赛

鉴于您的示例案例,您希望区分测试输入间隔和基本条目,因为不应在算法结果中拆分测试输入间隔。

我将首先在更一般的设置中描述一个解决方案,其中基本和测试输入间隔被视为相同。然后,该解决方案将得到补充,以满足保留的测试条目。

此补充假设测试输入间隔不重叠。第二个修改确保在运行算法之前重叠的测试输入间隔在概念上被折叠成单个间隔。

警告

此解决方案不提供收缩包装的 Python 实现,而是提供伪代码。不过,将其转换为有效的 python 应该很简单。

大纲

原始时间间隔涵盖一天中的特定部分pp 是这些区间的并集。观察到这个并集是一组有序的非重叠区间。

基本思想是通过满足 3 个条件的一组不同的时间间隔来覆盖 p

  • 区间精确覆盖p
  • 间隔不重叠
  • 原始集合的区间边界不能是新集合区间内部的元素。

考虑原始间隔集的边界时间集。该集合中的元素是规范排序的。从该集合中构建由相邻时间点定义的间隔序列。结果是包含p 的非空区间的无间隙并列。

由于p可能不连续,所以构造的序列必须与p相交才能得到最终结果。这个路口没有
更改计算集中的任何间隔;但是,它可能会消除一些。

算法(一般)

  • 收集列表tl_ta 中的所有时间间隔边界。 tl_ta 的元素应该是实际时间和指示元素是间隔开始还是结束的标志的对。该标志将用于跟踪p 中的空白。

  • 按元素对的第一个组件对列表进行排序

  • 迭代排序的时间点列表,构建非重叠间隔的结果列表。

    要实现这一点,您需要...

    • 结果列表tl_cover(最初为空)。
    • 当前和上一个迭代元素ta_currentta_previous
    • 一个计数器 cnt_active 指示当前时间点包含多少个间隔(最初为 0)。

    在迭代的每个步骤中,都会执行以下操作:

    • 如果当前时间点和上一个时间点不同,并且有一些活动间隔,则在结果列表中推送一个新的时间间隔:

      if (ta_previous[0] != ta_current[0]) && cnt_active > 0 then tl_cover.push ( [ta_previous[0], ta_current[0]] ); end if

    • 如果当前时间是间隔开始,则增加活动间隔的计数器:
      if ta_current[1] then cnt_active++; end if;
    • 如果当前时间是间隔结束,则减少活动间隔的计数器:
      if !ta_current[1] then cnt_active--; end if;
    • 更新当前和上一个时间点:
      ta_previous = ta_current; ta_current = shift tl_ta;

算法(补充 1 - 稳定的测试进入间隔)

要保持测试输入间隔不变,请在迭代期间维护一个标志b_preserve,以指示测试输入间隔当前是否处于活动状态。在此期间,不会终止新的间隔。

因此来自tl_ta 的元组变成了三元组,第三个组件指示此时间点是否源于测试条目。处理测试条目开始/结束时间设置/重置b_preserve

tl_ta 应该按字典顺序排序比较元组(tl_ta[_][0], !tl_ta[_][1], tl_ta[_][1] ? tl_ta[_][2] : !tl_ta[_][2]),这样在相同的时间,...

  • ...首先处理区间上限
  • ...在区间下限中,首先处理测试条目边界
  • ...在区间上限中,测试条目边界最后处理

此约定可防止具有共同边界的多个测试和基准进入间隔之间的干扰。

现在每次迭代的第一步还检查测试条目的包含情况:

 if (ta_previous[0] != ta_current[0]) && (cnt_active > 0) && !b_preserve then
     tl_cover.push ( [ta_previous[0], ta_current[0]] );
 end if

新变量与活动间隔计数器一起维护:

 if ta_current[2] then
     b_preserve = ta_current[1];
 end if;

算法(补充 2 - 重叠测试输入间隔)

使用初始化为 0 的计数器 cnt_active_te 代替布尔值 b_preserve 来处理重叠。每次迭代第一步的新检查:

 if (ta_previous[0] != ta_current[0]) && (cnt_active > 0) && (cnt_active_te == 0) then
     tl_cover.push ( [ta_previous[0], ta_current[0]] );
 end if

维护cnt_active_te 而不是b_preserve

 if ta_current[2] then
     cnt_active_te += ta_current[1] ? 1 : -1;
 end if;

【讨论】:

  • 谢谢,我去看看
【解决方案2】:

通常 - 提供完整的代码不是一个好主意 - 我同意 - 但这里有一些东西,看起来很有效。基本思想是 1)首先将input_start带到test_start(如果它们都不相等并且input_start为min) 2) 总是取 test_start 和 test_end 3) 如果 test_end 小于 input_end(并且 end_input 和 end_test 不相等),则取 test_end 到 input_end。

下面的代码用一些花里胡哨的东西来做到这一点。

def time_to_int(time_str, midnight_0=False):
    """ Converts a given time to an int. if midnight_0 is passed and is
    True, returns 0, instead of 24
    """
    time_str = time_str.lower()
    if time_str.find('am') > 0:
        return int(time_str.strip().strip('am'))
    elif time_str.find('pm') > 0:
        if midnight_0:
            return (12 + int(time_str.strip().strip('pm'))) % 24
        else :
            return (12 + int(time_str.strip().strip('pm')))
    else :
        return -1

def int_to_ampm(time):
    if(time <= 12):
        return str(time) + 'am'
    else:
        return str(time-12) + 'pm'

# throw in some tests for the above routine
#print time_to_int(' 9am')
#print time_to_int(' 9AM')
#print time_to_int(' 11Pm')
#print time_to_int('12pM\n')
#print int_to_ampm(12)
#print int_to_ampm(11)
#print int_to_ampm(17)

input_entries = ['9am - 5pm', '9am - 10am']
test_entries = ['10am - 12am', '8am - 10am', '8am - 11am']

for ip in input_entries:
    for test in test_entries:
        ip_1 = [time_to_int(x) for x in ip.split('-')]
        test_1 = [time_to_int(x) for x in test.split('-')]
        out_intervals = []
        start = min(ip_1[0], test_1[0])
        end = max(ip_1[1], test_1[1])
        if(start == ip_1[0]) and start != test_1[0]:
            out_intervals.append([start, test_1[0]])
        out_intervals.append(test_1)
        if(end == ip_1[1]) and (ip_1[1] != test_1[1]):
            out_intervals.append([test_1[1], end])
        print ip_1, test_1, "=>",  ["-".join(y) for y in [map(int_to_ampm, x) for x in out_intervals]]

【讨论】:

    【解决方案3】:

    我会这样做:

    import time
    
    Entry1 = "9am - 5pm"
    Test_Entry = "10am - 12am"
    
    #Split the test into hours
    hours = Entry1.split(" - ")
    Test_hours = Test_Entry.split(" - ")
    
    #Convert the hours into time objects for comparison
    hours24 = [ time.strptime(hour, '%I%p') for hour in hours]
    Test_hours24 = [ time.strptime(hour, '%I%p') for hour in Test_hours]
    
    #Case 1
    if Test_hours24[0] >= hours24[0] and Test_hours24[1] <= hours24[1]:
        print time.strftime('%I%p',hours24[0]) + " - " + time.strftime('%I%p',Test_hours24[0]) + ",  " + time.strftime('%I%p',Test_hours24[0]) + " - " + time.strftime('%I%p',Test_hours24[1]) + ",  " + time.strftime('%I%p',Test_hours24[1]) + " - " + time.strftime('%I%p',hours24[1])
    
    #Case 2
    elif Test_hours24[0] <= hours24[0] and Test_hours24[1] <= hours24[1]:
        print time.strftime('%I%p',Test_hours24[0]) + " - " + time.strftime('%I%p',Test_hours24[1]) + ",  " + time.strftime('%I%p',Test_hours24[1]) + " - " + time.strftime('%I%p',hours24[1])
    
    #Case 3
    elif Test_hours24[0] <= hours24[0] and Test_hours24[1] >= hours24[1]:
        print time.strftime('%I%p',Test_hours24[0]) + " - " + time.strftime('%I%p',Test_hours24[1])
    
    else:
        print "Error"
    

    【讨论】:

    • 我认为你错过了问题的重点。问题不在于将字符串拆分为时间对象。时间已经在时间对象中,上午 9 点表示 start_time 并且破折号 - 仅用于说明目的。我想知道分割重叠时间的逻辑
    猜你喜欢
    • 2023-01-30
    • 2015-07-17
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    • 2015-01-07
    相关资源
    最近更新 更多