web-dev-qa-db-ja.com

Pythonで2つの順序付けられていないリスト(セットではない)を効率的に比較するにはどうすればよいですか?

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

aとbは等しいと見なされる必要があります。これらはまったく異なる要素を持っているため、順序が異なるだけです。

実は、実際のリストは整数ではなくオブジェクト(クラスインスタンス)で構成されています。

119
johndir

O(n)Counter()メソッドが最適です(オブジェクトがハッシュ可能な場合):

def compare(s, t):
    return Counter(s) == Counter(t)

O(n log n)sorted()メソッドは次善(オブジェクトが注文可能な場合):

def compare(s, t):
    return sorted(s) == sorted(t)

O(n * n):オブジェクトがハッシュ可能でも順序付け可能でもない場合は、等価性を使用できます。

def compare(s, t):
    t = list(t)   # make a mutable copy
    try:
        for elem in s:
            t.remove(elem)
    except ValueError:
        return False
    return not t
205

両方をソートできます:

sorted(a) == sorted(b)

並べ替えのカウント もより効率的です(ただし、オブジェクトがハッシュ可能である必要があります)。

>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
14
Mark Byers

アイテムが常にハッシュ可能であることがわかっている場合は、O(n)であるCounter()を使用できます
アイテムが常にソート可能であることがわかっている場合は、O [n log n)であるsorted()を使用できます

一般的なケースでは、ソートできることや要素があることに依存することはできないので、残念ながらO(n ^ 2)であるこのようなフォールバックが必要です。

len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)
11
John La Rooy

これを行う最良の方法は、リストを並べ替えて比較することです。 (Counterの使用は、ハッシュ可能でないオブジェクトでは機能しません。)これは整数の場合は簡単です:

sorted(a) == sorted(b)

任意のオブジェクトでは少し複雑になります。オブジェクトのアイデンティティ、つまりsameオブジェクトが両方のリストにあるかどうかを気にする場合は、id()関数をソートキーとして使用できます。

sorted(a, key=id) == sorted(b, key==id)

(Python 2.xでは、実際にkey=パラメーターは必要ありません。オブジェクトとオブジェクトを比較できるためです。順序は任意ですが、安定しているため、この目的には問題なく機能します。 ;オブジェクトの順序は関係なく、順序は両方のリストで同じですが、Python 3では、さまざまなタイプのオブジェクトを比較することは多くの状況で許可されていません。 、文字列を整数と比較することはできません。そのため、さまざまなタイプのオブジェクトがある場合は、オブジェクトのIDを明示的に使用することをお勧めします。

一方、リスト内のオブジェクトをvalue、で比較する場合は、最初にオブジェクトの「値」の意味を定義する必要があります。次に、それをキーとして(およびPython 3の場合は一貫した型として)提供する方法が必要になります。多くの任意のオブジェクトで機能する可能性のある1つの方法は、repr()でソートすることです。もちろん、これは多くの余分な時間と大きなリストなどのrepr()文字列を構築するメモリを浪費する可能性があります。

sorted(a, key=repr) == sorted(b, key==repr)

オブジェクトがすべて独自のタイプである場合、オブジェクトで__lt__()を定義して、オブジェクトが他のオブジェクトと比較する方法を知ることができます。そうすれば、key=パラメーターを心配することなく、それらを並べ替えることができます。もちろん、__hash__()を定義し、Counterを使用することもできます。

5
kindall

リストにハッシュできないアイテム(オブジェクトのリストなど)が含まれている場合、 Counter Class およびid()関数を使用できる場合があります。

from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
    print("Lists a and b contain the same objects")
3
Mars

テストコンテキストで比較を実行する場合は、 assertCountEqual(a, b)py>=3.2)および assertItemsEqual(a, b)2.7<=py<3.2)を使用します。

ハッシュできないオブジェクトのシーケンスでも動作します。

2
jarekwg

https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual

assertCountEqual(first、second、msg = None)

順序に関係なく、最初にシーケンスに2番目と同じ要素が含まれていることをテストします。そうでない場合、シーケンス間の違いをリストするエラーメッセージが生成されます。

重複する要素は、最初と2番目を比較するときに無視されません。各要素が両方のシーケンスで同じカウントを持っているかどうかを検証します。 assertEqual(Counter(list(first))、Counter(list(second)))と同等ですが、ハッシュできないオブジェクトのシーケンスでも機能します。

バージョン3.2の新機能。

または2.7の場合: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual

2
cleder

A、bリスト

def ass_equal(a,b):
try:
    map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
    if len(a) == 0: # if a is empty, means that b has removed them all
        return True 
except:
    return False # b failed to remove some items from a

それらをハッシュ可能にしたりソートしたりする必要はありません。

1
Umur Kontacı

あなたの場合、以下のコードが機能することを願っています:-

if ((len(a) == len(b)) and
   (all(i in a for i in b))):
    print 'True'
else:
    print 'False'

これにより、両方のリストabのすべての要素が、同じ順序であるかどうかに関係なく、確実に同じになります。

よりよく理解するには、 この質問 の私の答えを参照してください

1
Pabitra Pati