簡體   English   中英

在不使用嵌套循環的情況下查找列表中所有對的有效方法

[英]Efficient way to find all the pairs in a list without using nested loop

假設我有一個存儲許多二維點的列表。 在這個列表中,一些位置存儲了相同的點,將存儲相同點的位置的索引視為索引對。 我想找到列表中的所有對並返回所有 2 x 2 索引對。 列表中可能有一些點重復了兩次以上,但只需要將第一個匹配項視為一對。

例如,在下面的列表中,我總共有 9 個點,並且有 5 個位置包含重復點。 索引 0、3 和 7 存儲相同的點 ( [1, 1] ),索引 1 和 6 存儲相同的點 ( [2, 3] )。

[[1, 1], [2, 3], [1, 4], [1, 1], [10, 3], [5, 2], [2, 3], [1, 1], [3, 4]]

因此,對於此列表,我想將索引對返回為(索引 0,索引 3)和(索引 1,索引 6)。 我能想出的唯一解決方案是通過嵌套循環來實現,我將其編碼如下

A = np.array([[1, 1], [2, 3], [1, 4], [1, 1], [10, 3], [5, 2], [2, 3], [1, 1], [3, 4]], dtype=int)

# I don't want to modified the original list, looping through a index list insted.
Index = np.arange(0, A.shape[0], 1, dtype=int) 
Pair = [] # for store the index pair
while Index.size != 0:

    current_index = Index[0]
    pi = A[current_index]
    Index = np.delete(Index, 0, 0)

    for j in range(Index.shape[0]):
        pj = A[Index[j]]
        distance = linalg.norm(pi - pj, ord=2, keepdims=True)
        
        if distance == 0:
            Pair.append([current_index, Index[j]])
            Index = np.delete(Index, j, 0)
            break

雖然這段代碼對我有用,但時間復雜度是O(n^2) ,其中n == len(A) ,我想知道是否有更有效的方法來以較低的時間復雜度完成這項工作。 感謝您的任何想法和幫助。

您可以使用字典來跟蹤每個點的索引。

然后,您可以遍歷字典中的項目,打印出與多次出現的點對應的索引。 此過程的運行時間與A中的點數成線性關系,而不是二次關系:

points = {}

for index, point in enumerate(A):
    point_tuple = tuple(point)
    if point_tuple not in points:
        points[point_tuple] = []
    points[point_tuple].append(index)

for point, indices in points.items():
    if len(indices) > 1:
        print(indices)

這打印出來:

[0, 3, 7]
[1, 6]

如果您只想要出現點的前兩個索引,則可以使用print(indices[:2])而不是print(indices)

這類似於另一個答案,但由於在多對的情況下您只需要前兩個,因此您可以在一次迭代中完成。 在 dict 中的適當鍵下添加索引,並在(且僅當)有兩點時生成索引:

from collections import defaultdict

l = [[1, 1], [2, 3], [1, 4], [1, 1], [10, 3], [5, 2], [2, 3], [1, 1], [3, 4]]

def get_pairs(l):
    ind = defaultdict(list)

    for i, pair in enumerate(l):
        t = tuple(pair)
        ind[t].append(i)
        if len(ind[t]) == 2:
            yield list(ind[t])

list(get_pairs(l))
# [[0, 3], [1, 6]]

一個沒有循環的純 Numpy 解決方案迄今為止唯一的一個)是使用np.unique兩次,其中一個技巧是刪除在兩次搜索之間找到的第一個項目。 此解決方案假設可以設置標記(例如 -1,integer 的最小值,NaN),這通常不是問題(如果需要,您可以使用更大的類型)。

A = np.array([[1, 1], [2, 3], [1, 4], [1, 1], [10, 3], [5, 2], [2, 3], [1, 1], [3, 4]], dtype=int)

# Copy the array not to mutate it
tmp = A.copy()

# Find the location of unique values
pair1, index1 = np.unique(tmp, return_index=True, axis=0)

# Discard the element found assuming -1 is never stored in A
INT_MIN = np.iinfo(A.dtype).min
tmp[index1] = INT_MIN

# Find the location of duplicated values
pair2, index2 = np.unique(tmp, return_index=True, axis=0)

# Extract the indices that share the same pair of values found
left = index1[np.isin(pair1, pair2).all(axis=1)]
right = index2[np.isin(pair2, pair1).all(axis=1)]

# Combine the each left index with each right index
result = np.hstack((left[:,None], right[:,None]))

# result = array([[0, 3],
#                 [1, 6]])

該解決方案應在O(n log n)時間內運行,因為np.unique在內部使用基本排序(更具體地說是快速排序)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM