提问者:小点点

删除列表中一半重复项的有效方法


如果我有一个列表,例如L=[1,8,8,8,1,3,3,8],并且保证每个元素出现偶数次,那么我如何制作一个L的所有元素现在出现N/2次的列表。 因此,由于1发生了2次,现在应该发生一次。 因为8出现4次,所以现在应该出现两次。 由于3发生了两次,所以应该发生一次。

因此新列表将类似于k=[1,8,8,3]

做这件事最快的方法是什么? 我为每个元素都做了list.count(),但是非常慢。


共3个答案

匿名用户

如果顺序不重要,一种方法就是只在排序之后获取奇数或偶数索引。 这些列表将是相同的,所以你只需要其中的一个。

l = [1,8,8,8,1,3,3,8]
l.sort()

# Get all odd indexes
odd = l[1::2]

# Get all even indexes
even = l[::2]

print(odd)
print(odd == even)

结果:

[1, 3, 8, 8]
True

匿名用户

使用计数器跟踪每个元素的计数

from collections import Counter
l = [1,8,8,8,1,3,3,8]
res = []
count = Counter(l) # its like dict(1: 2, 8: 4, 3: 2)
for key, val in count.items():
    res.extend(val//2 * [key])
print(res)
# output
[1, 8, 8, 3]

匿名用户

由于您保证列表的每个元素都是2的倍数,因此在构建输出列表时构建计数器会更快,而不是先构建计数器(或排序),然后再使用它。

l = [1,8,8,8,1,3,3,8]
count={}
res=[]
for i in l:
  if i in count: count[i]+=1
  else: count[i]=1
  if count[i]%2: res.append(i)

print(res)

输出量

[1,8,8,3]

编辑比较每种方法的时间/费用

使用TimeIt模块表明,这种方法比首先使用计数器快2.7倍。

即。

def one():
  l = [1,8,8,8,1,3,3,8]
  count={}
  res=[]
  for i in l:
    if i in count: count[i]+=1
    else: count[i]=1
    if count[i]%2: res.append(i)

  #print(res)


def two():
  from collections import Counter
  l = [1,8,8,8,1,3,3,8]
  res = []
  count = Counter(l) # its like dict(1: 2, 8: 4, 3: 2)
  for key, val in count.items():
    res.extend(val//2 * [key])

o=timeit.Timer(one)

t=timeit.Timer(two)

print(o.timeit(100000))

print(t.timeit(100000))

print(o.timeit(100000))

print(t.timeit(100000))

输出(秒)

0.28666
0.80822
0.28678
0.80113

如果顺序不重要,那么Wimanicesir的方法将被优先考虑,其加速比为4倍,结果为0.07037(比计数器方法快11倍)。

我怀疑在two(无序)中使用counter方法可能会在导入时出现明显的膨胀或减慢,因此我测试了“先计数,后编译结果”方法,同时使用这里的简单方法从one(有序)进行计数

count={}
for i in l:
  if i in count: count[i]+=1
  else: count[i]=1

它比counter快得多。 在定义的两个测试中替换计数器会导致0.31而不是0.80的时间。 但是,在计数过程中,编译(有序)结果的速度稍微快一些,如two中所示。 对于无序的结果,使用Wimanicesir的方法要快得多。