在比较列表中的元素时,如何有效地迭代并提高 O(n^2) 的时间复杂度?

Posted

技术标签:

【中文标题】在比较列表中的元素时,如何有效地迭代并提高 O(n^2) 的时间复杂度?【英文标题】:When making comparison between the elements in a list, how to efficiently iterate and improve the time complexity from O(n^2)? 【发布时间】:2022-01-23 01:37:29 【问题描述】:

我有一个列表,我想在其中比较列表中的每个元素。我知道我们可以使用嵌套循环来做到这一点,但时间复杂度是 O(n^2)。有没有什么办法可以提高时间复杂度并提高比较效率?

例如:

我有一个列表,我想在其中找到每个元素之间的数字差异。考虑一个列表 array=[100,110,010,011,100] 我试图找出每个整数之间数字的差异。 array[0] 与 array[4] 相同(即 100 和 100),而 array[0] 有 1 位与 array[1] 不同(即 100 和 110),而 array[0] 有 3 位是不同于数组 [3](即 100 和 011)。假设相似的整数被定义为具有相同或数字差异仅为 1 的整数,我想返回一个列表作为输出,其中每个元素表示具有相似数字的整数(即数字差异

对于输入列表array=[100,110,010,011,100],我的预期输出应该是[2,3,2,1,2] .在输出列表中, output[0] 表示 array[0] 类似于 array[1] 和 array[4] (即类似于 100 ,我们在列表中还有 2 个其他整数 110,100)

这是我的代码,虽然非常低效 O(n^2):

def diff(a,b):
    difference= [i for i in range(len(a)) if a[i]!=b[i]]
    return len(difference)

def find_similarity_int(array):
    # write your code in Python 3.6
    res=[0]*len(array)
    string=[]
    for n in array:
        string.append(str(n))
    for i in range(0,len(string)):
        for j in range(i+1,len(string)):
            count=diff(string[i],string[j])
            if(count<=1):
                res[i]=res[i]+1
                res[j]=res[j]+1

    return res

input_list=['100','110','010','011','100']
output=find_similarity_int(input_list)
print("The similarity metrics for the given list is : ",output)

输出:

给定列表的相似度指标是:[2, 3, 2, 1, 2]

谁能建议一种有效的方法来进行比较,最好只有 1 个循环?谢谢!

【问题讨论】:

由于您正在进行 n*n 比较(根据定义,您应该将每个元素与每个其他元素进行比较),您似乎无法做到这一点比 n^2 快。作业是否表明您应该能够做到?如果这里有一个聪明的技巧,我猜它涉及改变计算 diff 的方式(例如,以某种方式一次在整个列表中逐位而不是逐个元素地进行计算)。 肯定有办法让代码更优雅——你可以跳过重新转换为str,你可以将diff缩短为sum(i != j for i, j in zip(a, b)),你的主嵌套循环会使用enumerate 看起来更好。但这些都不会改变时间复杂度。 是的,这个问题是在在线评估中提出的,但不幸的是,我想不出更有效的解决方案。您如何一次在整个列表中逐位进行比较?有什么例子吗? 我正在设想类似将每个数字在 O(n) 中每个位置的出现求和,然后使用它以某种方式推断(也在 O(n) 中)每个字符串与列表中的所有其他内容。例如。在位置 0 有 3 个项目 1,因此这些项目在该位置与其他 2 个字符串不同。但我不认为聚合技巧很有效,因为你不知道差异在哪里重叠。 感觉有点像en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm 【参考方案1】:

如果值仅是二进制数字,则可以使用多重集(集合中的计数器)获得 O(nxm) 解决方案(其中 m 是值的宽度)。使用多重集中的值计数,将每个数字中恰好对应于一位变化的项目计数相加(加上重复的数量):

from collections import Counter
def simCount(L):
    counts = Counter(L)  # multiset of distinct values / count
    result = []
    for n in L:
        r = counts[n]-1                              # duplicates
        for i,b in enumerate(n):                     # 1 bit changes
            r += counts[n[:i]+"01"[b=="0"]+n[i+1:]]  # count others
        result.append(r)                             # sum of similars
    return result

输出:

A = ['100','110','010','011','100']

print(simCount(A)) # [2, 3, 2, 1, 2]

为避免对每个项目进行字符串操作,您可以将它们转换为整数并使用按位运算符进行 1 位更改:

from collections import Counter
def simCount(L):
    bits   = [1<<i for i in range(len(L[0]))] # bit masks
    L      = [int(n,2) for n in L]            # numeric values
    counts = Counter(L)                       # multiset n:count
    result = []
    for n in L:
        result.append(counts[n]-1)            # duplicates
        for b in bits:                        # 1 bit changes
            result[-1] += counts[b^n]         # sum similars
    return result

A = ['100','110','010','011','100']

print(simCount(A)) # [2, 3, 2, 1, 2]

【讨论】:

我正要发布第二个解决方案。虽然我的情况稍微复杂一些,因为我没有直觉到 counts[b^n] 快​​捷方式。做得很好。 太好了,非常感谢!

以上是关于在比较列表中的元素时,如何有效地迭代并提高 O(n^2) 的时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章

如何从给定列表有效地构造 B+ 树?

如何迭代地将 2 个列表中的元素应用到新函数中? [复制]

如何有效地将元素插入数组的任意位置?

如何有效地计算帕斯卡三角形中的一行?

如何比较时间复杂度小于 O(n^2) 的两个数组中的每个元素

用于迭代列表中步骤的 Big-O 表示法-Python