如何有效地找到从每个值到下一个较低/较高值的距离?

Posted

技术标签:

【中文标题】如何有效地找到从每个值到下一个较低/较高值的距离?【英文标题】:How can I efficiently find distances from each value to the next lower/higher value? 【发布时间】:2022-01-21 06:45:49 【问题描述】:

我会告诉你我正在使用什么结构,请随时推荐任何更改,如 numpy 数组或其他东西。

无论如何,我所拥有的是与股票价格相对应的 500 万个连续条目的列表。

然后我还有 2 个列表,每个列表的长度相同 - 500 万个条目。这些列表对应于预期的“上限”和预期的“下限”,我预计库存将从序列中的那个点达到。

我要做的是遍历下限列表中的所有 500 万个条目,并按顺序记录价格最终达到下限所需的时间。然后我想对上限列表做同样的事情。

以下是仅包含 10 个条目的股票价格表的潜在解决方案示例:

prices =       [15,16,18,22,23,17,15,19,15,18]
upper_limits = [17,18,21,23,25,22,18,21,18,20]
lower_limits = [14,15,16,18,19,15,13,17,14,16]


solved_upper = [2,1,1,1,x,x,1,x,1,x]
solved_lower = [x,5,4,2,1,1,x,1,x,x]

#I think I got this right?  Anyways as you can see, the solved lists simply show
#how many entries we have to look at until we find a value that is >= to it for upper, or <= to it
#for lower

所以问题是,对于大量的条目,如何合理快速地解决这个问题? (实际上,我有 10 个上限列表和 10 个下限列表......所以需要更高的效率)

【问题讨论】:

我试着写一个标题来描述你的问题。有什么不准确的地方吗? ...也就是说,开放式问题(例如寻求解决问题的最佳算法)在这里通常不被视为主题——Stack Overflow 专注于狭隘的、具体的问题(理想情况下)可以接受规范的答案,并且很少有可能证明算法是最好的选择。我并没有对自己投反对票/近距离投票,但不要感到惊讶。 @CharlesDuffy 我认为“询问可以为此使用什么数据结构”是一个非常合理的编程问题。 【参考方案1】:

我要清楚效率。用真实的数据对象替换 Dictionary 对象可能是个好主意。

首先,我们需要将您的时间序列变成可搜索的树。

def make_tree (series, i=None, j=None):
    if i is None:
        i = 0
    if j is None:
        j = len(series) - 1

    if i == j:
        return 
            "min_i": i,
            "max_i": i,
            "min_value": series[i],
            "max_value": series[i],
            "left": None,
            "right": None
        
    else:
        mid = (i + j) // 2
        left = make_tree(series, i, mid)
        right = make_tree(series, mid+1, j)
        return 
            "min_i": i,
            "max_i": j,
            "min_value": min(left['min_value'], right['min_value']),
            "max_value": max(left['max_value'], right['max_value']),
            "left": left,
            "right": right
        

接下来我们需要函数来搜索那棵树:

def find_next_after_at_least(tree, min_i, min_value):
    if tree['max_i'] <= min_i or tree['max_value'] < min_value:
        return None
    elif tree['min_i'] == tree['max_i']:
        return tree['min_i'] - min_i
    else:
        answer = find_next_after_at_least(tree['left'], min_i, min_value)
        if answer is None:
            answer = find_next_after_at_least(tree['right'], min_i, min_value)
        return answer

def find_next_after_at_most(tree, min_i, max_value):
    if tree['max_i'] <= min_i or max_value < tree['min_value']:
        return None
    elif tree['min_i'] == tree['max_i']:
        return tree['min_i'] - min_i
    else:
        answer = find_next_after_at_most(tree['left'], min_i, max_value)
        if answer is None:
            answer = find_next_after_at_most(tree['right'], min_i, max_value)
        return answer

现在您的搜索可以轻松编写:

def solve_upper(tree, limits):
    return [
        find_next_after_at_least(tree, i, limits[i])
            for i in range(len(limits))
    ]

def solve_lower(tree, limits):
    return [
        find_next_after_at_most(tree, i, limits[i])
            for i in range(len(limits))
    ]

现在你的示例问题:

t = make_tree([15,16,18,22,23,17,15,19,15,18])
print(solve_upper(t, [17,18,21,23,25,22,18,21,18,20]))
print(solve_lower(t, [14,15,16,18,19,15,13,17,14,16]))

【讨论】:

【参考方案2】:

您可以使用类似于“单调队列”的数据结构有效地解决这个问题(在 O(N log N) 时间内)。你可以用谷歌搜索,但通常的用例与你的完全不同,所以我只解释一下。 (奇怪的是,这是我一周内在这里看到的第三个问题,答案需要这样的结构。)

在您的情况下,您将从价格数组的末尾开始工作,将每个价格添加到单调队列的前面。每次你输入一个价格,其他一些可能会被丢弃,因此队列只保存比之前所有的都大的项目。这些是唯一可能成为“下一个更高价格”的项目。它们在队列中也是单调递增的,因此您可以使用二分搜索找到第一个 >= 目标。由于您需要知道下一个较高值的索引,因此您可以存储索引而不是值本身。

这就解决了上限问题。下限是类似的,但队列是单调递减的。

在python中是这样的:

def solve_upper(prices, limits):
    solved = [0]*len(prices)
    q = [0]*len(prices)
    qstart = len(q)
    for i in range(len(prices)-1, -1, -1):
        price = prices[i]
        while qstart < len(q) and prices[q[qstart]] <= price:
            # the price at the start of q needs to be discarded, since
            # it isn't greater than the new one
            qstart += 1
        # prepend the new price
        qstart -= 1
        q[qstart] = i
        limit = limits[i]

        # binary search to find the first price >= limit
        minpos = qstart
        maxpos = len(q)
        while minpos < maxpos:
            testpos = minpos + (maxpos - minpos)//2
            if prices[q[testpos]] < limit:
                # too low
                minpos = testpos+1
            else:
                # high enough
                maxpos = testpos
        if minpos < len(q):
            solved[i] = q[minpos]-i
        else:
            solved[i] = None
    return solved

def solve_lower(prices, limits):
    solved = [0]*len(prices)
    q = [0]*len(prices)
    qstart = len(q)
    for i in range(len(prices)-1, -1, -1):
        price = prices[i]
        while qstart < len(q) and prices[q[qstart]] >= price:
            # the price at the start of q needs to be discarded, since
            # it isn't less than the new one
            qstart += 1
        # prepend the new price
        qstart -= 1
        q[qstart] = i
        limit = limits[i]

        # binary search to find the first price <= limit
        minpos = qstart
        maxpos = len(q)
        while minpos < maxpos:
            testpos = minpos + (maxpos - minpos)//2
            if prices[q[testpos]] > limit:
                # too low
                minpos = testpos+1
            else:
                # high enough
                maxpos = testpos
        if minpos < len(q):
            solved[i] = q[minpos]-i
        else:
            solved[i] = None
    return solved

prices =       [15,16,18,22,23,17,15,19,15,18]
upper_limits = [17,18,21,23,25,22,18,21,18,20]
lower_limits = [14,15,16,18,19,15,13,17,14,16]
print(solve_upper(prices, upper_limits))
print(solve_lower(prices, lower_limits))

输出:

[2, 1, 1, 1, None, None, 1, None, 1, None]
[None, 5, 4, 2, 1, 1, None, 1, None, None]

注意:如果您将此答案与 @btilly 的答案进行对比,请在评论中包含结果!

【讨论】:

以上是关于如何有效地找到从每个值到下一个较低/较高值的距离?的主要内容,如果未能解决你的问题,请参考以下文章

空间数据:计算点与最大点值的距离并绘图

大小端存储

如何从一个活动中获取edittext值到下一个活动的recyclerview?

我如何在熊猫数据框中找到每个月的“ n”个最大值?

将php数组值排序或显示为钟形曲线

React Native、IBECONS、RSSI 值到距离的转换