it-swarm.com.de

Bestimmen Sie, ob 2 Listen unabhängig von der Reihenfolge dieselben Elemente haben

Entschuldigung für die einfache Frage, aber es fällt mir schwer, die Antwort zu finden.

Wenn ich zwei Listen miteinander vergleiche, möchte ich wissen, ob sie "gleich" sind, da sie den gleichen Inhalt haben, jedoch in unterschiedlicher Reihenfolge.

Ex:

x = ['a', 'b']
y = ['b', 'a']

Ich möchte, dass x == y zu True ausgewertet wird.

84
toofly

Sie können einfach prüfen, ob die Multisets mit den Elementen von x und y gleich sind:

import collections
collections.Counter(x) == collections.Counter(y)

Dies erfordert, dass die Elemente hashierbar sind. Die Laufzeit ist in O(n), wobei n die Größe der Listen ist.

Wenn die Elemente auch eindeutig sind, können Sie auch in Mengen konvertieren (gleiche asymptotische Laufzeit, in der Praxis möglicherweise etwas schneller):

set(x) == set(y)

Wenn die Elemente nicht hashierbar, aber sortierbar sind, gibt es eine andere Alternative (Laufzeit in O(n log n))

sorted(x) == sorted(y)

Wenn die Elemente weder hashbar noch sortierbar sind, können Sie die folgende Hilfsfunktion verwenden. Beachten Sie, dass es ziemlich langsam ist (O(n²)) und im Allgemeinen nicht außerhalb des esoterischen Falls von nicht-waschbaren und unsortierbaren Elementen verwendet werden sollte.

def equal_ignore_order(a, b):
    """ Use only when elements are neither hashable nor sortable! """
    unmatched = list(b)
    for element in a:
        try:
            unmatched.remove(element)
        except ValueError:
            return False
    return not unmatched
124
phihag

Stellen Sie fest, ob 2 Listen unabhängig von der Reihenfolge die gleichen Elemente haben?

Schlussfolgerung aus Ihrem Beispiel:

x = ['a', 'b']
y = ['b', 'a']

dass die Elemente der Listen nicht wiederholt werden (sie sind eindeutig) sowie hashable (was Strings und andere bestimmte unveränderliche Python-Objekte sind), die direkteste und rechnerisch effizienteste Antwort(Dies sind semantisch wie mathematische Sets, die Sie in der Schule gelernt haben). 

set(x) == set(y) # prefer this if elements are hashable

Falls die Elemente hashierbar, aber nicht eindeutig sind, funktioniert collections.Counter auch semantisch als Multiset, aber es ist viel langsamer:

from collections import Counter
Counter(x) == Counter(y)

Lieber sorted verwenden:

sorted(x) == sorted(y) 

wenn die Elemente bestellbar sind. Dies würde nicht eindeutige oder nicht-hashable Umstände berücksichtigen, dies könnte jedoch viel langsamer sein als die Verwendung von Sets.

Empirisches Experiment

Ein empirisches Experiment kommt zu dem Schluss, dass man set vorziehen sollte, dann sorted. Entscheiden Sie sich für Counter, wenn Sie andere Dinge wie Zählungen oder die weitere Verwendung als Multiset benötigen.

Erstes Setup:

import timeit
import random
from collections import Counter

data = [str(random.randint(0, 100000)) for i in xrange(100)]
data2 = data[:]     # copy the list into a new one

def sets_equal(): 
    return set(data) == set(data2)

def counters_equal(): 
    return Counter(data) == Counter(data2)

def sorted_lists_equal(): 
    return sorted(data) == sorted(data2)

Und testen:

>>> min(timeit.repeat(sets_equal))
13.976069927215576
>>> min(timeit.repeat(counters_equal))
73.17287588119507
>>> min(timeit.repeat(sorted_lists_equal))
36.177085876464844

Wir sehen also, dass das Vergleichen von Sets die schnellste Lösung ist und das Vergleichen von sortierten Listen die zweitschnellste ist.

13
Aaron Hall

Dies scheint zu funktionieren, obwohl es bei großen Listen möglicherweise umständlich ist.

>>> A = [0, 1]
>>> B = [1, 0]
>>> C = [0, 2]
>>> not sum([not i in A for i in B])
True
>>> not sum([not i in A for i in C])
False
>>> 

Wenn jedoch jede Liste muss alle Elemente anderer Elemente enthält, ist der obige Code problematisch. 

>>> A = [0, 1, 2]
>>> not sum([not i in A for i in B])
True

Das Problem tritt auf, wenn len(A) != len(B) und in diesem Beispiel len(A) > len(B). Um dies zu vermeiden, können Sie eine weitere Anweisung hinzufügen.

>>> not sum([not i in A for i in B]) if len(A) == len(B) else False
False

Eine weitere Sache: Ich habe meine Lösung mit timeit.repeat verglichen, und zwar unter den gleichen Bedingungen wie Aaron Hall in seinem Posten. Wie vermutet, sind die Ergebnisse enttäuschend. Meine Methode ist die letzte. set(x) == set(y) ist es.

>>> def foocomprehend(): return not sum([not i in data for i in data2])
>>> min(timeit.repeat('fooset()', 'from __main__ import fooset, foocount, foocomprehend'))
25.2893661496
>>> min(timeit.repeat('foosort()', 'from __main__ import fooset, foocount, foocomprehend'))
94.3974742993
>>> min(timeit.repeat('foocomprehend()', 'from __main__ import fooset, foocount, foocomprehend'))
187.224562545
1
blahreport

Wie in den obigen Kommentaren erwähnt, ist der allgemeine Fall ein Schmerz. Es ist ziemlich einfach, wenn alle Elemente hashierbar sind oder alle Elemente sortierbar sind. Allerdings musste ich vor kurzem versuchen, den allgemeinen Fall zu lösen. Hier ist meine Lösung. Nach dem Posting wurde mir klar, dass dies ein Duplikat einer Lösung ist, die ich beim ersten Durchgang nicht gesehen habe. Wenn Sie jedoch Slices anstelle von list.remove () verwenden, können Sie unveränderliche Sequenzen vergleichen.

def sequences_contain_same_items(a, b):
    for item in a:
        try:
            i = b.index(item)
        except ValueError:
            return False
        b = b[:i] + b[i+1:]
    return not b
0
Grahame