将列表的值与同一列表中具有声誉的其他值进行比较

问题描述

提供了一个列表

a=[3,7,4,2,0]

我想将列表中的每个元素与列表中的所有元素进行比较

所以对于第一个数字3,我希望将其与3,0进行比较,如果3小于或等于元素将1添加到空白列表中

重复此步骤将给出列表

b=[3,1,5] 这意味着对于第一个数字3,列表a中有3个数字小于或等于a的元素

我尝试使用itertools.combination进行比较,但是并没有比较相同的声誉。

另一种方法是使用两个for循环并制作一个方矩阵进行比较,但这不起作用(花费很长时间才能获得结果)

解决方法

探讨了三种方法

代码

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,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时counter_set_method变得不同

性能

  • bisect_method(tobias_k)算法总体性能最佳
  • counter_set_method接近bisect_method(tobias_k),但对于大数组会变得更好
  • counter_method最糟糕的是直到大阵列

测试代码

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

Benchmarks

,

比较所有元素对,它们将是O(n²)。相反,您可以对列表进行排序,然后使用bisect模块对sorted数组进行二进制搜索以找到正确的位置,以插入元素并从该位置获取较大元素的数量。复杂度是O(nlogn)。

>>> import bisect
>>> a = [3,0]
>>> b = sorted(a)
>>> [len(a) - bisect.bisect_left(b,x) for x in a]
[3,1,5]

({bisect_left(b,x)就是在排序列表中插入元素的位置,即元素的数量比x小;因为您需要元素x小于或等于,则需要减去len(a)

这对于重复的元素也有效,并且产生与简单的O(n²)方法相同的结果:

>>> a = [3,4]
>>> b = sorted(a)
>>> [len(a) - bisect.bisect_left(b,x) for x in a]
[4,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,3]
,

为避免多次比较,可以使用以下内容:

b = [sorted(a,reverse=True).index(i)+1 for i in a]