Python: 如何在两个不同的数组中找到两个相等/最接近的值?

8

假设我们有两个长度相等的数组:

arr1 = (21, 2, 3, 5, 13)
arr2 = (10, 4.5, 9, 12, 20)

arr1中哪个变量与arr2中的变量相等/最接近

看着这两个列表,我们可以很容易地说出距离最近的数字是4.55。我试图实现一个函数,在给定两个列表的情况下返回两个最接近的值,它在上面的例子中有点起作用,但这只是勉强算是一个解决方案,因为它不是最优的。当我们稍微更改数组时,您可以轻松检查到该函数失败:

arr1 = (21, 2, 3, 5, 13)
arr2 = (10, 4.5, 9, 12, 18)

这个函数返回的值是13和18。
以下是该函数的代码:
def get_nearest(arr1, arr2):
    lr = [[0, 0, 0]]
    for x1 in arr1:
        for x2 in arr2:
            r = (x1 / x2 % (x1 + x2))
            print x1, x2, r
            if r <= 1 and r >= lr[0][2]:
                lr.pop()
                lr.append([x1, x2, r])
    return lr

你能想出更好的吗?

1
对我来说,5和4.5似乎是最接近的? - Tanveer Alam
你是对的,是我犯了错。 - minerals
5个回答

10
速度是否是一个问题?您关心连接吗?如果不是,那么像这样的简单解决方案如何?
from itertools import product
sorted(product(arr1, arr2), key=lambda t: abs(t[0]-t[1]))[0]

对于两者而言

arr1 = (21, 2, 3, 5, 13)
arr2 = (10, 4.5, 9, 12, 20)

并且

arr1 = (21, 2, 3, 5, 13)
arr2 = (10, 4.5, 9, 12, 18)

这将产生
(5, 4.5)

解释:

product(arr1, arr2) = [(a1, a2) for (a1, a2) in product(arr1, arr2)]

产生一个包含所有 N**2 对数字的列表:
[(21, 10), (21, 4.5), ..., (13, 12), (13, 20)]

然后我们使用sorted按绝对差值(|a1 - a2|)对它们进行排序。通过传递key关键字参数给sorted,我们告诉sorted使用排序标准lambda t: abs(t[0] - t[1])。具有最小绝对差异的一对被置于排序数组的第一个索引中,因此我们可以通过在末尾添加[0]来抓取它。 编辑: 如评论中Piotr建议的那样,您可以向minmax提供key=func,这将大大加快速度。改用以下方式试试:
from itertools import product
min(product(arr1, arr2), key=lambda t: abs(t[0]-t[1]))[0]

确实不错,但我需要一些时间来理解这里发生了什么。 - minerals
@minerals,我添加了一个解释。 - wflynny
1
你应该使用max而不是sorted()[0]。 - Piotr Dabkowski

7

这是我能写出的最快算法,它的时间复杂度为n*log(n),比其他答案中呈现的朴素n*n方法要快得多。它在处理之前对数组进行排序(这是最耗时的部分),然后尝试最小化差异(在最坏情况下需要2*n):

def closest_array_items(a1, a2):
    if not a1 or not a2:
        raise ValueError('Empty array')
    a1, a2  = iter(sorted(a1)), iter(sorted(a2))
    i1, i2 = a1.next(), a2.next()
    min_dif = float('inf')
    while 1:
        dif = abs(i1 - i2)
        if dif < min_dif:
             min_dif = dif
             pair = i1, i2
             if not min_dif:
                  break
        if i1 > i2:
            try:
                i2 = a2.next()
            except StopIteration:
                break
        else:
            try:
                i1 = a1.next()
            except StopIteration:
                break
    return pair

1
>>> arr1 = (21, 2, 3, 5, 13)
>>> arr2 = (10, 4.5, 9, 12, 20)
>>> for a1 in arr1:
...     for a2 in arr2:
...         if a1 > a2:
...             result.append([a1, a2, a1-a2])
...         else:
...             result.append([a1, a2, a2-a1])
>>> sorted(result, key=lambda i:i[-1])[0][:2]
[5, 4.5]

一个简单的方法是获取两个数组之间的差异,根据它们的差异进行排序,并获取第一个元素。
>>> sorted([[a1,a2,a1-a2] if(a1>a2) else [a1,a2,a2-a1] for a1 in arr1 for a2 in arr2], key=lambda i:i[-1])[0][:2]
[5, 4.5]

1

那么,简单地保存每次迭代中的差异和值如何?

arr1 = (21, 2, 3, 5, 13)
arr2 = (10, 4.5, 9, 12, 20)

diff = float("inf")

for a1 in arr1:
    for a2 in arr2:
        if abs(a1-a2) < diff:
            diff = abs(a1-a2)
            values = (a1, a2)

print(values)

1

这里有一个函数,可以在0.01秒内解决长度为1000、2000的两个向量的问题:

def get_closest_elements(arr_1, arr_2):
    """
    The function finds the two closest elements in two arrays

    Returns
    -------
    idx_1 : int
        index of element in arr_1
    idx_2 : int
        index of element in arr_2
    min_diff : float
        minimal difference between arrays
    """

    # get array with all differences between arrays
    diff_arr = x[:, np.newaxis] - y

    # get absolute value
    diff_arr = np.abs(diff_arr)

    # get minimum difference
    min_diff = np.min(diff_arr)

    # get the indexes for the elements of interest in arr_1 and arr_2
    idx_1, idx_2 = np.where(diff_arr == min_diff)

    return idx_1, idx_2, min_diff


# apply function
x = np.array([21, 2, 3, 5, 13])
y = np.array([10, 4.5, 9, 12, 20])
# n = 1000
# x = np.random.rand(n)
# y = np.random.rand(2*n)

idx_1, idx_2, min_diff = get_closest_elements(x, y)
print "x{} - y{} = {}".format(idx_1, idx_2, min_diff)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接