简体   繁体   English

总和超过阈值的最小区间

[英]Smallest interval whose sum exceeds a threshold

In a non-negative 1D array x , we wish to find the shortest slice such that x[start:end].sum() > threshold , with the optional constraint that start <= argmax(x) < end .在非负一维数组x中,我们希望找到满足x[start:end].sum() > threshold的最短切片,可选约束为start <= argmax(x) < end Must work with floats and integers.必须使用浮点数和整数。

Is there a known algorithm to accomplish this?是否有已知的算法来完成此操作? I have implemented one here , but it's not as fast as I wish, and ideally there shouldn't be a heavyweight dependency (eg numba).我在这里实现了一个,但它没有我希望的那么快,理想情况下不应该有重量级依赖(例如 numba)。 Example:例子:

x = [0, 1, 2, 3, 4, 9, 7, 1, 0, 0, 0]
threshold = 3 + 4 + 9 + 7 - .01

start, end = func(x, threshold)
print(x[start:end])
[3 4 9 7] 

@גלעד ברקן's answer is acceptably fast in numba; @גלעד ברקן 的回答在 numba 中的速度是可以接受的; my implem . 我的实现 Though, non-numba is strongly preferred, meaning minimizing if , for , etc - unless someone can point to a lightweight numba...虽然,非 numba 是强烈推荐的,这意味着最小化iffor等 - 除非有人可以指出轻量级的 numba ...

This reminds me of the problem of finding the smallest window that includes all members of a set, which can be solved in O(n) time and O(k) space with two pointers.这让我想起了找最小的包含集合所有成员的window这个问题,可以用两个指针在O(n)的时间和O(k)的空间内解决。 In our case, O(1) space would seem to be enough since we're only interested in a sum.在我们的例子中,O(1) 空间似乎就足够了,因为我们只对求和感兴趣。

Move the right pointer until the sum is achieved.移动右指针直到求和。 Move the left pointer just until the sum is again too low.移动左指针直到总和再次过低。 Then move the right pointer, etc. Addressing the argmax constraint left to the reader.然后移动右指针等。解决argmax约束留给读者。

Just my few cents:只是我的几分钱:

  1. Because the array is non-negative the list containing running sum at each index is already a sorted list.因为数组是非负的,所以在每个索引处包含运行总和的列表已经是一个排序列表。

  2. Compute running sum at each index and construct a list of tuples (running_sum, current_index).计算每个索引处的运行总和并构建一个元组列表(running_sum,current_index)。

  3. If the running sum at an index exceeds the threshold look for the greatest value that is less than (running_sum - threshold) in the list of running sums excluding the one at current index using binary search如果索引处的运行总和超过阈值,则使用二进制搜索在运行总和列表中查找小于 (running_sum - threshold) 的最大值,不包括当前索引处的那个

  4. Now the current range is the [index_found_by_binary_search+1, curr_index]现在当前范围是 [index_found_by_binary_search+1, curr_index]

  5. Minimize upon all such ranges.最小化所有这些范围。

In your example: the running sum is: [0, 1, 3, 6, 10, 19, 26, 27, 27, 27, 27]在您的示例中:运行总和为: [0, 1, 3, 6, 10, 19, 26, 27, 27, 27, 27]

The sum(26) exceeds threshold of 22.99 at index = 6 sum(26) 在索引 = 6 时超过阈值 22.99

Now difference between threshold and sum is 26 - 22.99 = 3.01.现在阈值和总和之间的差异是 26 - 22.99 = 3.01。

Look for the greatest value that is smaller than 3.01 - this can be done by binary search.寻找小于 3.01 的最大值- 这可以通过二分查找来完成。 We need greatest value because we want to minimize the range.我们需要最大的价值,因为我们想要最小化范围。

The greatest value is 3 that is smaller than 3.01 and can be found at index 2.最大值为 3,小于 3.01,可以在索引 2 处找到。

The result is the range [2+1, 6] = [3,6]结果是范围 [2+1, 6] = [3,6]

Now do this all along the array and minimize upon the length of the range.现在沿着数组执行此操作并最小化范围的长度。

Not sure if that is what you already did.不确定那是否是您已经做过的。

Thanks to @גלעד ברקן for the pointers.感谢@גלעד ברקן 的指点。 Numba/plain Python is here (just add @jit ), with greedy validation, and below's Cython ( numpy tutorial ), which equals Numba's speed on float64 and is 10% faster for float32 when len(x)=1e6 . Numba/plain Python 在这里(只需添加@jit ),带有贪婪验证,下面是 Cython( numpy 教程),它等于 Numba 在float64上的速度,并且在len(x)=1e6float32快 10%。 They're x10 faster than the fastest numpy-vectorized implem I could come up with.它们比我能想到的最快的 numpy-vectorized implem 快 10 倍。

Note, for float32 and long sequences, it suffers from roundoff imprecision, and instead one should compute the sum as (though it's slower):请注意,对于float32和长序列,它会受到舍入不精确的影响,而应该将总和计算为(尽管速度较慢):

cs = np.hstack([0., np.cumsum(x)])
sm = cs[end] - cs[start]
import cython

float_int = cython.fused_type(cython.floating, cython.integral)

@cython.wraparound(False)
cpdef int smallest_interval_over_threshold(float_int[:] x, float_int threshold):
    cdef Py_ssize_t N = x.shape[0]
    cdef float_int[:] x_view = x

    cdef Py_ssize_t left = 0
    cdef Py_ssize_t right = 1
    cdef Py_ssize_t min_size = N
    cdef float_int sm = x_view[left]

    while right < N:
        if sm > threshold:
            min_size = min(min_size, right - left)
            sm -= x_view[left]
            left += 1
        else:
            sm += x_view[right]
            right += 1

    while left < N and sm > threshold:
        min_size = min(min_size, right - left)
        sm -= x[left]
        left += 1

    cdef int min_size_int = <int>min_size
    return min_size_int

The argmax constraint is easily satisfied by bounding left and right appropriately, and of course we can fetch the exact indices easily once we know the right interval. argmax约束很容易通过right left来满足,当然,一旦我们知道正确的间隔,我们就可以很容易地获取准确的索引。 Unlike Numba, Cython's lightweight while retaining Python's syntax minus typing (at least for my case).与 Numba 不同,Cython 的轻量级同时保留了 Python 的语法减去输入(至少对我来说是这样)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM