繁体   English   中英

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

[英]comparing a value of a list with other values in the same list with reputation

给出一个列表

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.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, 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 之前达到 max(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

基准

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

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

(只是bisect_left(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]

为避免多重比较,您可以使用以下方法:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM