0

リストから同じアイテムを削除するにはどうすればよいですか? 言う

A= [1, 2, 3, 8, 7, 8, 8, 7, 6]

そして、8つすべてを削除したいのですが、どうすればよいですか? 番号が順番になっていない場合は?

4

3 に答える 3

4

これを行う簡単な方法は、そうでないすべてのアイテムを含む新しいリストを作成すること8です。例えば:

B=[item for item in A if item != 8]

本当にその場で実行したい場合 (たとえば、他のオブジェクトが と同じリストへの参照を持っていて、A他のオブジェクトに変更を表示したい場合など) は可能です。しかし、それはよりトリッキーです。たとえば、インデックスで削除する場合、ある時点で戻る必要があります (最初の を削除すると、8のすべての に新しいインデックスがあるため、常に最後のものを最初に削除する必要があるため): 8

indices = [index for index, item in enumerate(A) if item==8]
for index in reversed(indices):
    del A[index]

removeまたは、失敗するまで試行し続けることもできますが、これは醜く遅いです。

while True:
    try:
        A.remove(8)
    except ValueError:
        break

実際、新しいリストを作成してから、その新しいリストのコピーに変更する方よい場合がよくあります。A

A[:]=[item for item in A if item != 8]

パフォーマンス テストのために、提案されたすべての回答に対してこのコードを実行しました。テストされたアルゴリズムは次のとおりです。

  • revind: この回答での最初のインプレース アルゴリズムです。
  • whiledel: Chris Barker の最初のアルゴリズム。
  • スライド: Chris Barker の 2 番目のアルゴリズム (修正版)。
  • Genexpr: 私の最後のアルゴリズムですが、listcomp の代わりに Genexpr を使用しています。
  • listcomp: 私の最後のアルゴリズム。
  • filterer: 私の最後のアルゴリズムですが、filter を使用しています (これは、2.x ではリストを構築することを意味しますが、3.x では Genexpr のような反復子を構築することに注意してください)。

実際に変更する必要がない場合A(通常は変更する必要がない場合は、名前を再バインドするだけで済みます)、コピーしてから変更するアルゴリズムはさらに単純で高速になることに注意してください。しかし、ここではテストしませんでした。

また、「while True: remove until error」アルゴリズム、または Chris Barker が提案したコメントのバリエーションも完全にはテストしませんでした。ただし、非常に簡単なテストでは、予想どおり、変動が約 2 倍遅く、ほとんどすべてのテスト ケースで他の何よりも桁違いに遅いことが示されています。

いずれにせよ、このテストでは、0 から 16、256、または 65536 までの範囲の値を持つ、長さ 100K または 1M (担当者数の 1/10) のランダム リストから 0 を削除しています (個別の値が少ないほど、削除するヒット)。

CPython を使用している場合、特に N が大きい場合は、listcomp バージョンが常に最速です。PyPy を使用している場合、N が巨大で M が小さい場合、インプレース アルゴリズムはそれを打ち負かすことができますが、その場合、それらはすべて非常に高速です。ファンシー スライド アルゴリズムは、他のインプレース アルゴリズムの 2 次動作を排除しますが、単純なケースでは動作が遅くなるため、明確な勝者となる場所は実際にはありません。(これは最も複雑な方法でもあります。最初の試行で唯一正しくなかったのはおそらくそのためです。) 絶対に削除するコピーの数が少ない場合は、 PyPy を使用している場合は、whiledel ソリューションを検討してください。他のユースケース、またはユースケースがよくわからない場合は、listcomp を使用します。

          64-bit python CPython 3.3.0
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.188 17.3   0.085 1.23   0.074 0.080
whiledel  0.324 19.3   0.206 1.36   0.199 0.203
slide     0.091  0.54  0.097 0.54   0.095 0.538
genepxr   0.094  0.11  0.100 0.11   0.099 0.108
listcomp  0.070  0.08  0.073 0.08   0.071 0.079
filterer  0.081  0.09  0.080 0.09   0.835 0.088

          64-bit python CPython 2.7.2
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.198 17.1   0.089 1.23   0.088 0.955
whiledel  0.345 19.8   0.233 1.36   0.234 0.243
slide     0.095  0.54  0.099 0.55   0.095 0.551
genepxr   0.092  0.11  0.097 0.11   0.107 0.116
listcomp  0.091  0.09  0.099 0.08   0.105 0.114
filterer  0.122  0.23  0.132 0.09   0.135 0.150

          64-bit python PyPy 1.9.0 (Python 2.7.2)
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.266 28.5   0.027 1.97   0.018 0.013
whiledel  0.281 30.2   0.023 1.94   0.034 0.009
slide     0.022  0.39  0.015 0.022  0.006 0.018
genepxr   0.089  0.13  0.087 0.154  0.089 0.147
listcomp  0.052  0.08  0.057 0.073  0.052 0.073
filterer  0.054  0.07  0.053 0.078  0.048 0.074

スライドのパフォーマンスを予測するのは少し難しいです。N が大きく M が小さい場合、whiledel で吹き飛ばされると予想されますが、実際には遅くなります。しかし、よく考えてみると、このアルゴリズムは、M 個の線形移動を N 個の単純なコピーに効果的に置き換えます。前者は O(NM) で後者は O(N) ですが、C でのループ (または、さらに良いのは の内部memmove) での乗数は、Python でのループよりもはるかに小さいため、N/M でない限り無視できません。巨大です(その時点では、すべてのソリューションが非常に高速であるため、ほとんど問題になりません). したがって、M Python ループと NM C ループを実行すると、N Python ループを簡単に打ち負かすことができます。

于 2013-08-01T22:48:39.607 に答える
2

これらの回答のほとんどは、データのコピーを提案しています。リストが十分に大きい場合、これは望ましくない場合があります。簡単に使用できます

while 8 in A: A.remove(8)

データをコピーせずに実行します。ただし、これは二次時間で実行されるため、リストが大きい場合には望ましくありません。データをコピーせずに線形時間で実行するには、次を使用します。

def remove_all_from_list(L, n):
    i = 0
    while i < len(L):
        if L[i] == n:
            del L[i] # Do not increment i here, because L[i] will change
        else:
            i += 1

>>> A = [1, 2, 3, 8, 7, 8, 8, 7, 6]
>>> remove_all_from_list(A, 8)
>>> A
[1, 2, 3, 7, 7, 6]

編集: @abarnert は、del L[i] が O(N) であることを思い出させたので、これは実際には 2 次です。これは、O(N) インプレース ソリューションの別の試みです...

def remove_all_from_list(L, n):
    # indices takes *worse-case* O(N) space, but typical-case much less
    indices = [i for i, x in enumerate(L) if x==n]
    indices_seen = 0
    num_indices = len(indices)
    for i in xrange(len(L)):
        while (indices_seen < num_indices and 
            i + indices_seen == indices[indices_seen]):
            indices_seen += 1
        if i+indices_seen >= len(L):
            break
        L[i] = L[i+indices_seen]            
    L[-indices_seen:] = []

これにより、最後にすべてのシャッフルが行われるため、各要素は最大で 1 回移動されます。これは、abarnert のコピー方法とまったく同じくらい時間がかかることを認識しています。非常に大きなリストがある場合に備えて、メモリ使用量を減らす方法を考えているところです。


最終編集: 速度テスト (@abarnert のものほど包括的ではありません)

import random
L = range(30)*10000
random.shuffle(L)
from copy import copy

for fn in [remove_1, remove_2, remove_3, remove_4, remove_5, remove_6]:
    print fn.__name__
    %timeit fn(copy(L), 8)

remove_1 # listcomp
10 loops, best of 3: 39.1 ms per loop
remove_2 # revind
1 loops, best of 3: 1.7 s per loop
remove_3 # try: remove; except: break
1 loops, best of 3: 65.7 s per loop
remove_4 # while n in L: L.remove(n)
1 loops, best of 3: 129 s per loop
remove_5 # whiledel
1 loops, best of 3: 1.87 s per loop
remove_6 # slide
1 loops, best of 3: 227 ms per loop
于 2013-08-02T18:02:12.530 に答える
0
In [13]: A= [1, 2, 3, 8, 7, 8, 8, 7, 6]

In [14]: [i for i in A if i!=8]
Out[14]: [1, 2, 3, 7, 7, 6]

In [15]: filter(lambda i: i!=8, A)
Out[15]: [1, 2, 3, 7, 7, 6]
于 2013-08-01T22:48:33.250 に答える