【问题标题】:Deciding if all intervals are overlapping确定所有区间是否重叠
【发布时间】:2021-02-18 17:34:22
【问题描述】:

我正在做一个问题,n 个人站在一条线上,每个人都知道自己的位置和速度。我被要求找到让所有人去任何地点的最短时间。

基本上,我正在做的是使用二分搜索找到最短时间,并让每个 ith 人在该时间间隔内走的最远距离。如果所有的间隔都重叠,那么就有一个每个人都可以去的地方。

我有这个问题的解决方案,但是我的错误解决方案超出了时间限制,无法找到间隔。我当前的解决方案运行速度太慢,我希望得到更好的解决方案。

我的代码:

    people = int(input())
    peoplel = [list(map(int, input().split())) for _ in range(people)] # first item in people[i] is the position of each person, the second item is the speed of each person
    def good(time):
        return checkoverlap([[i[0] - time *i[1], i[0] + time * i[1]] for i in peoplel])
        # first item,second item = the range of distance a person can go to 
 

    def checkoverlap(l):
        for i in range(len(l) - 1):
            seg1 = l[i]
            for i1 in range(i + 1, len(l)):
                seg2 = l[i1]
                if seg2[0] <= seg1[0] <= seg2[1] or seg1[0] <= seg2[0] <= seg1[1]:
                    continue
                elif seg2[0] <= seg1[1] <= seg2[1] or seg1[0] <= seg2[1] <= seg1[1]:
                    continue
                return False
        return True

(这是我第一次问问题,所以请告诉我有什么问题)

【问题讨论】:

  • 如果每个人在不同时间到达现场怎么办?
  • @btilly 说的不够清楚,interval是一个人在那个时间段可以走的距离范围,时间是每个人到达那个点的最短时间。所以,如果所有的时间间隔都重叠,那么每个人都可以在步行 x 次后到达那个地方,那么我只是搜索每个人可以去那个地方的最短时间。
  • 我建议您尝试使用en.wikipedia.org/wiki/Simplex_algorithm。您的线性不等式是每个人都可以达到某个时间点。您正在尝试优化时间。
  • 为什么这么复杂?它似乎更像是算法的任务。 LP 可能有点矫枉过正。

标签: python-3.x algorithm performance optimization intervals


【解决方案1】:

一个确实是线性的

在我完成答案后不久,我发现了一个简化,它消除了排序的需要,从而使我们能够进一步降低查找所有区间是否重叠到 O(N) 的复杂性。

如果我们查看初始排序后正在执行的步骤,我们可以看到我们基本上是在检查

if max(lower_bounds) < min(upper_bounds):
    return True
else:
    return False

由于minmax 都是线性的,不需要排序,我们可以通过以下方式简化算法:

  • 创建下限数组 - 一次。
  • 创建一个上限数组 - 一次。
  • 进行我上面提到的比较 - 两次遍历新数组。

所有这些都可以一次性完成以进一步优化(并防止一些不必要的内存分配),但是为了解释的目的,这更清楚。

由于关于正确性和时间的推理在上一次迭代中已经完成,我将跳过它并保留下面的部分,因为它很好地展示了优化背后的思考过程。

一种统治他们的方式

免责声明:本节已被上述内容按时间淘汰。但是,由于它实际上使我能够找出线性解决方案,所以我将其保留在这里。

正如标题所说,排序是一种相当简单的方法。它将需要一些不同的数据结构 - 而不是将每个间隔保存为(min, max),我选择将每个间隔保存为(min, index), (max, index)。 这使我可以按 minmax 值对它们进行排序。接下来是对排序数组的一次线性传递。我们还创建了一个 False 值的辅助数组。这些表示在开始时所有区间都是闭合的。
现在是对数组的传递:

  • 由于数组是排序好的,我们首先遇到每个区间的min。在这种情况下,我们增加openInterval 计数器和间隔本身的True 值。间隔现在是开放的 - 在我们关闭间隔之前,该人可以到达聚会 - 我们在他(或她)的范围内。
  • 我们沿着阵列前进。只要我们打开间隔,一切都很好,如果我们设法打开所有间隔,我们就有了聚会的目的地,所有的社交距离都会崩溃。如果发生这种情况,我们会返回 True
  • 如果我们关闭任何间隔,我们就会发现我们的破坏者无法再成功了。 (或者我们可以讨论那些破坏派对的人是那些在有人必须离开时还没来的人)。我们返回 False

由此产生的复杂性是 O(Nlog(N)) 由初始排序引起的,因为 pass 本身本质上是线性的。这比“成对检查所有区间”方法导致的原始 O(n^2) 好很多。

代码:

import numpy as np
import cProfile, pstats, io

#random data for a speed test. Not that useful for checking the correctness though.
testSize = 10000
x = np.random.randint(0, 10000, testSize)
y = np.random.randint(1, 100, testSize)
peopleTest = [x for x in zip(x, y)]

#Just a basic example to help with the reasoning about the correctness
peoplel = [(1, 2), (3, 1), (8, 1)]
# first item in people[i] is the position of each person, the second item is the speed of each person


def checkIntervals(people, time):
    a = [(x[0] - x[1] * time, idx) for idx, x in enumerate(people)]
    b = [(x[0] + x[1] * time, idx) for idx, x in enumerate(people)]
    checks = [False for x in range(len(people))]
    openCount = 0
    intervals = [x for x in sorted(a + b, key=lambda x: x[0])]
    for i in intervals:
        if not checks[i[1]]:
            checks[i[1]] = True
            openCount += 1
            if openCount == len(people):
                return True
        else:
            return False

    print(intervals)



def good(time, people):
    return checkoverlap([[i[0] - time * i[1], i[0] + time * i[1]] for i in people])
    # first item,second item = the range of distance a person can go to


def checkoverlap(l):
    for i in range(len(l) - 1):
        seg1 = l[i]
        for i1 in range(i + 1, len(l)):
            seg2 = l[i1]
            if seg2[0] <= seg1[0] <= seg2[1] or seg1[0] <= seg2[0] <= seg1[1]:
                continue
            elif seg2[0] <= seg1[1] <= seg2[1] or seg1[0] <= seg2[1] <= seg1[1]:
                continue
            return False
    return True


pr = cProfile.Profile()
pr.enable()

print(checkIntervals(peopleTest, 10000))

print(good(10000, peopleTest))

pr.disable()
s = io.StringIO()
sortby = "cumulative"
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())

具有 10K 随机值的通过测试数组的分析统计数据:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.001    0.001    8.933    8.933 (good)
    1    8.925    8.925    8.926    8.926 (checkoverlap)
    1    0.003    0.003    0.023    0.023 (checkIntervals)
    1    0.008    0.008    0.010    0.010 {built-in method builtins.sorted}

【讨论】:

  • 谢谢!您的解决方案比我解决这个问题的蛮力方法要快得多。没想到这个办法解决。
猜你喜欢
  • 1970-01-01
  • 2011-06-10
  • 2017-06-28
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
  • 2019-12-02
相关资源
最近更新 更多