0

私は2つのリストを持っています。

a = [1,2,3]
b = [1,2,3,1,2,3]

リスト b から 1、2、3 を削除したいのですが、すべてではありません。結果のリストには次のものが含まれている必要があります。

b = [1,2,3]

私は現在持っています:

for element in a:
    try:
        b.remove(element)
    except ValueError:
        pass

ただし、これは a と b が非常に大きくなるとパフォーマンスが低下します。同じ結果を得るより効率的な方法はありますか?

編集

「すべての出現ではない」ことを明確にするために、a には「1」が 1 つしかないため、b から両方の「1」を削除したくないことを意味します。

4

3 に答える 3

2

私はこれを行います:

set_a = set(a)
new_b = []
for x in b:
  if x in set_a:
    set_a.remove(x)
  else:
    new_b.append(x)

他の set ソリューションとは異なり、これは順序を維持しbます (気にする場合)。

于 2012-10-13T02:54:11.803 に答える
1

私はこのようなことをします:

from collections import defaultdict

a = [1, 2, 3]
b = [1, 2, 3, 1, 2, 3]

# Build up the count of occurrences in b
d = defaultdict(int)
for bb in b:
    d[bb] += 1

# Remove one for each occurrence in a
for aa in a:
    d[aa] -= 1

# Create a list for all elements that still have a count of one or more
result = []
for k, v in d.iteritems():
    if v > 0:
        result += [k] * v

または、もう少しあいまいにしたい場合は、次のようにします。

from operator import iadd

result = reduce(iadd, [[k] * v for k, v in d.iteritems() if v > 0], [])

defaultdict は、各キーの出現回数を生成します。から構築されるbと、 でキーが出現するたびに減分されaます。次に、まだ残っている要素を出力して、それらが複数回発生するようにします。

defaultdict は python 2.6 以降で動作します。それ以降の python (2.7 以降だと思います) を使用している場合は、collections.Counter.


後で: これを一般化して、カウンター スタイルの defaultdict の減算を作成することもできます。

from collections import defaultdict
from operator import iadd

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

def build_dd(lst):
    d = defaultdict(int)
    for item in lst:
        d[item] += 1
    return d

def subtract_dd(left, right):
    return {k: left[k] - v for k, v in right.iteritems()}

db = build_dd(b)
da = build_dd(a)
result = reduce(iadd,
                [[k] * v for k, v in subtract_dd(db, da).iteritems() if v > 0],
                [])

print result

しかし、そのreduce表現は今ではかなりあいまいです。


さらに後で: python 2.7 以降では、 を使用するcollections.Counterと、次のようになります。

from collections import Counter

base = [1, 2, 3]
missing = [4, 5, 6]
extra = [7, 8, 9]
a = base + missing
b = base * 4 + extra

result = Counter(b) - Counter(a)
print result
assert result == dict([(k, 3) for k in base] + [(k, 1) for k in extra])
于 2012-10-13T02:45:07.413 に答える
1

一般に、常に list.remove() を避けたいと思うでしょう (その通りです。パフォーマンスが著しく低下します)。また、リスト内よりも辞書またはセット内の要素を検索する方がはるかに高速 (O(1)) です。したがって、リスト1からセットを作成します(順序が重要でない場合は、リスト2から)。

このようなもの:

sa = set(a)
new_b = [x for x in b if not x in sa]
# here you created a 3d list but I bet it's OK.

ただし、削除する要素を選択するための実際のアルゴリズムが何であるかはわかりません。「しかし、すべてのオカレンスではない」について詳しく説明してください。

于 2012-10-13T02:51:02.973 に答える