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