Python 将列表中的某个值与同一列表中的其他值进行信誉比较
给你一张单子Python 将列表中的某个值与同一列表中的其他值进行信誉比较,python,list,compare,Python,List,Compare,给你一张单子 a=[3,7,4,2,0] 我想将列表中的每个元素与列表中的所有元素进行比较 对于第一个数字3,我希望它与3,7,4,2,0进行比较,如果3小于或等于元素,那么在空白列表中添加1 重复此步骤,它将给出 b=[3,1,2,4,5] 这意味着对于第一个数字3,列表a中有3个数字小于或等于a的元素 我尝试使用itertools.composition进行比较,但是它不能比较相同的声誉 另一种方法是使用两个for循环并生成一个方形矩阵进行比较,但是这不起作用(需要花费太长的时间才能得到结果
a=[3,7,4,2,0]
我想将列表中的每个元素与列表中的所有元素进行比较
对于第一个数字3,我希望它与3,7,4,2,0进行比较,如果3小于或等于元素,那么在空白列表中添加1
重复此步骤,它将给出
b=[3,1,2,4,5]
这意味着对于第一个数字3,列表a中有3个数字小于或等于a的元素
我尝试使用itertools.composition进行比较,但是它不能比较相同的声誉
另一种方法是使用两个for循环并生成一个方形矩阵进行比较,但是这不起作用(需要花费太长的时间才能得到结果)不要比较所有的元素对,这将是O(n²)。相反,您可以对列表进行排序,然后使用模块对排序后的数组进行二进制搜索,以找到插入元素的正确位置,并从该位置获取较大元素的数量。其复杂性为O(nlogn)
(左二等分(b,x)
将是在排序列表中插入元素的位置,即小于x
的元素数;由于需要x
的元素数小于或等于,因此需要len(a)
减去该值。)
这也适用于重复元素,并产生与简单O(n²)方法相同的结果:
>>> a = [3, 7, 4, 2, 0, 4]
>>> b = sorted(a)
>>> [len(a) - bisect.bisect_left(b, x) for x in a]
[4, 1, 3, 5, 6, 3]
>>> [sum(1 for y in a if x<=y) for x in a] # just for reference, do not use this
[4, 1, 3, 5, 6, 3]
>a=[3,7,4,2,0,4]
>>>b=已排序(a)
>>>[len(a)-对分。a中x的左对分(b,x)]
[4, 1, 3, 5, 6, 3]
>>>[sum(如果x中y为1)为避免多次比较,可以使用以下方法:
b = [sorted(a, reverse=True).index(i)+1 for i in a]
探索了三种方法
- 函数计数器\u方法:从修改代码
- 函数计数器集方法:对tobias_k提出的计数器集方法的改进
- 对分法:托拜厄斯法
代码
import bisect
def bisect_method(a):
b = sorted(a)
return [len(a) - bisect.bisect_left(b, x) for x in a]
def counter_method(arr):
# Count of values in array
cnts = {i:0 for i in range(max(arr)+1)}
for i in range(len(arr)):
cnts[arr[i]] += 1
# Store the sum of cnts of elements
# greater than the current eleement
cnt_ge = 0
for k, cnt in reversed(cnts.items()):
cnts[k] = cnt_ge + cnts[k]
cnt_ge = cnts[k]
# cnts[arr[k]] has count of greater or equal to
return [cnts[x] for x in arr]
# Improvement to counter_method
# initialize cnts using set of array values
# as suggested by tobias_k
def counter_set_method(arr):
# Count of values in array
cnts = {i:0 for i in set(arr)}
for i in range(len(arr)):
cnts[arr[i]] += 1
# Store the sum of cnts of elements
# greater than the current eleement
cnt_ge = 0
for k, cnt in reversed(cnts.items()):
cnts[k] = cnt_ge + cnts[k]
cnt_ge = cnts[k]
# cnts[arr[k]] has count of greater or equal to
return [cnts[x] for x in arr]
验证
断言未触发,因此结果相等
for a in [[3, 7, 4, 2, 0], [3, 7, 4, 2, 0, 4]]:
assert bisect_method(a) == counter_method(a) == counter_set_method(a)
from random import randint
a = [randint(0, 10**5) for _ in range(10**6)]
assert bisect_method(a) == counter_method(a) == counter_set_method(a)
注:
- 至少在10**5之前,相同结果达到最大值(arr)
- 未知为什么当arr变为10**6时计数器设置方法会不同
性能
import bisect
def bisect_method(a):
b = sorted(a)
return [len(a) - bisect.bisect_left(b, x) for x in a]
def counter_method(arr):
# Count of values in array
cnts = {i:0 for i in range(max(arr)+1)}
for i in range(len(arr)):
cnts[arr[i]] += 1
# Store the sum of cnts of elements
# greater than the current eleement
cnt_ge = 0
for k, cnt in reversed(cnts.items()):
cnts[k] = cnt_ge + cnts[k]
cnt_ge = cnts[k]
# cnts[arr[k]] has count of greater or equal to
return [cnts[x] for x in arr]
# Improvement to counter_method
# initialize cnts using set of array values
# as suggested by tobias_k
def counter_set_method(arr):
# Count of values in array
cnts = {i:0 for i in set(arr)}
for i in range(len(arr)):
cnts[arr[i]] += 1
# Store the sum of cnts of elements
# greater than the current eleement
cnt_ge = 0
for k, cnt in reversed(cnts.items()):
cnts[k] = cnt_ge + cnts[k]
cnt_ge = cnts[k]
# cnts[arr[k]] has count of greater or equal to
return [cnts[x] for x in arr]
- 对分法(tobias_k)算法总体性能最佳
- 计数器集方法与对分法(tobias_k)接近,但在大型阵列中效果更好
- 在大型阵列之前最差的计数器方法
测试代码
基准
阅读此内容:这并不是唯一地进行多重比较,但其复杂性甚至比比较所有元素对还要糟糕……而且,如果列表中有重复的元素,索引可能不起作用。很好,但是为什么范围(max(arr))
?您不能只进行cnts={i:0 for i in set(arr)}
或使用计数器
?我认为如果max>>len
,这应该更快,但可能需要对聚合阶段的值进行排序,这将使其为O(nlogn)而不是O(m)…还有,你使用的是哪一版本的Python?对我来说,它说dict项是不可逆的…@tobias_k——3.8.3版,好的,我的盒子上有3.6版。但是,对于一个包含1000个随机数的列表,我得到了很多不同的结果,最多100000个:对分为~550µs,计数器为20ms(!)(但对于设置了的计数器只有300µs)事实上,我刚刚注意到你做了randint(0,10*6)
,也就是说你的max
只有60!我猜你的意思是10**6
?
from random import randint
import numpy as np
import benchit # https://pypi.org/project/benchit/
funcs = [counter_method, counter_set_method, bisect_method]
inputs = [[randint(0, 10**5) for _ in range(x)] for x in 10**np.arange(7)]
t = benchit.timings(funcs, inputs)
print(t)
t.plot(logy=True, logx=True)
Functions counter_method counter_set_method bisect_method
Len
1 0.008725 0.000003 0.000001
10 0.035918 0.000008 0.000004
100 0.038195 0.000066 0.000051
1000 0.041830 0.000670 0.000717
10000 0.048070 0.007392 0.009392
100000 0.113914 0.096876 0.144737
1000000 0.708296 0.813105 2.653164