简体   繁体   English

在循环有序列表中查找最接近值的优雅方法

[英]An elegant way of finding the closest value in a circular ordered list

Given a sorted list such as [1.1, 2.2, 3.3] and a bounding value such as math.pi*2 , return the closest value for any given value from [0 - math.pi*2) 给定一个排序列表,如[1.1, 2.2, 3.3] math.pi*2 [1.1, 2.2, 3.3]和一个边界值,如math.pi*2 ,返回[0 - math.pi*2)任何给定值的最接近值

The function should return the index of the value, so that f(1.2) returns 0 while f(2.1) returns 1 , and f(6.0) should wrap around at math.pi*2 and return 0 , being closer to 1.1 than to 3.3 given the bounding value. 该函数应该返回值的索引,因此f(1.2)返回0f(2.1)返回1 ,而f(6.0)应该在math.pi*2math.pi*2并返回0 ,更接近于1.1而不是3.3给出边界值。 Just to be entirely explicit, this function should also wrap around on on the lower end, so that f(1.0, [5.0, 6.0], bound = math.pi*2) returns 1 . 为了完全明确,这个函数也应该在下端f(1.0, [5.0, 6.0], bound = math.pi*2) ,这样f(1.0, [5.0, 6.0], bound = math.pi*2)返回1

The use case is to map an arbitrary angle in radians to the nearest existing valid angle in the list. 用例是将弧度的任意角度映射到列表中最近的现有有效角度。 I've written this kind of function a couple of times in python with bisect , but the code always ends up offending my aesthetic senses. 我在python中用bisect写了几次这样的函数,但代码总是冒犯我的审美意识。 The high complexity and number of edge cases seems out of proportion with the intuitive simplicity of the function. 边缘情况的高复杂性和数量似乎与功能的直观简单性不成比例。 So I am asking if anyone can come up with a pleasing implementation, both in terms of efficiency and elegance. 所以我想问一下,无论是在效率还是优雅方面,是否有人都能提出令人满意的实施方案。

Here's a more elegant approach. 这是一种更优雅的方法。 Eliminate the edge cases by wrapping the number line around: 通过包裹数字线来消除边缘情况:

from bisect import bisect_right

def circular_search(points, bound, value):
    ##
    ## normalize / sort input points to [0, bound)
    points = sorted(list(set([i % bound for i in points])))
    ##
    ## normalize search value to [0, bound)
    value %= bound
    ##
    ## wrap the circle left and right
    ext_points = [i-bound for i in points] + points + [i+bound for i in points]
    ##
    ## identify the "nearest not less than" point; no
    ## edge cases since points always exist above & below
    index = bisect_right(ext_points, value)
    ##
    ## choose the nearest point; will always be either the
    ## index found by bisection, or the next-lower index
    if abs(ext_points[index]-value) >= abs(ext_points[index-1]-value):
        index -= 1
    ##
    ## map index to [0, npoints)
    index %= len(points)
    ##
    ## done
    return points[index]

As written, works unless inputs are wonky like no points, or bound==0. 正如所写的那样,除非输入像没有点,或者绑定== 0,否则它们会工作。

Use the bisect module as a base: 使用bisect模块作为基础:

from bisect import bisect_left
import math

def f(value, sorted_list, bound=math.pi * 2):
    value %= bound
    index = bisect_left(sorted_list, value)
    if index == 0 or index == len(sorted_list):
        return min((abs(bound + sorted_list[0] - value), 0), (abs(sorted_list[-1] - value), len(sorted_list) - 1))[1]
    return min((index - 1, index), 
        key=lambda i: abs(sorted_list[i] - value) if i >= 0 else float('inf'))

Demo: 演示:

>>> sorted_list = [1.1, 2.2, 3.3]
>>> f(1.2, sorted_list)
0
>>> f(2.1, sorted_list)
1
>>> f(6.0, sorted_list)
0
>>> f(5.0, sorted_list)
2

The easiest way would be just to use min: 最简单的方法就是使用min:

def angular_distance(theta_1, theta_2, mod=2*math.pi):
    difference = abs(theta_1 % mod - theta_2 % mod)
    return min(difference, mod - difference)

def nearest_angle(L, theta):
    return min(L, key=lambda theta_1: angular_distance(theta, theta_2))

In [11]: min(L, key=lambda theta: angular_distance(theta, 1))
Out[11]: 1.1

Making use of the sorted-ness of the list you can use the bisect module: 利用列表的排序,您可以使用bisect模块:

from bisect import bisect_left

def nearest_angle_b(theta, sorted_list, mod=2*math.pi):
    i1 = bisect_left(sorted_list, theta % mod)
    if i1 == 0:
        i1, i2 = len(sorted_list) - 1, 0
    elif i1 == len(sorted_list):
        i1, i2 = i1 - 1, 0
    else:
        i2 = (i1 + 1) % len(sorted_list)
    return min((angular_distance(theta, L[i], mod), i, L[i])
                 for i in [i1, i2])

Returns the distance, the index and the angle in the list to which it's closest to. 返回列表中距离最近的距离,索引和角度。

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

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