在比较列表中的元素时,如何有效地迭代并提高 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) 的时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章