给定已排序列表(例如[1.1, 2.2, 3.3]
)和限制值(例如math.pi*2
),返回[0 - math.pi*2)
该函数应该返回值的索引,以便f(1.2)
返回0
而f(2.1)
返回1
,而f(6.0)
应该返回{ {1}}并返回math.pi*2
,在给定边界值的情况下接近1.1而不是3.3。为了完全明确,这个函数也应该在下端回绕,以便0
返回f(1.0, [5.0, 6.0], bound = math.pi*2)
。
用例是将弧度的任意角度映射到列表中最近的现有有效角度。我在python中用1
写了几次这种函数,但代码总是冒犯我的审美意识。边缘情况的高复杂性和数量似乎与功能的直观简单性不成比例。因此,我想问是否有人能够在效率和优雅方面提出令人满意的实施方案。
答案 0 :(得分:2)
这是一种更优雅的方法。通过包裹数字线来消除边缘情况:
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]
正如所写,除非输入像没有点,或者绑定== 0。
,否则无效答案 1 :(得分:1)
使用bisect
module作为基础:
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'))
演示:
>>> 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
答案 2 :(得分:0)
最简单的方法就是使用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
利用列表的排序,您可以使用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])
返回列表中距离最近的距离,索引和角度。