Chcę zrobić coś podobnego do tego:
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
Ale nie jest to obsługiwane przez listy python Jaki jest najlepszy sposób na zrobienie tego?
Odpowiedzi:
Użyj zrozumienia listy:
Jeśli chcesz użyć
-
składni infix, możesz po prostu:możesz użyć go w następujący sposób:
Ale jeśli nie potrzebujesz absolutnie właściwości listy (na przykład zamawiania), po prostu użyj zestawów, które zalecają inne odpowiedzi.
źródło
list
dla nazw zmiennych, ponieważ przesłania tolist
konstruktor. Jeśli używasz „listy”, poprzedź ją podkreśleniem. Poza tym, upuszczając*
, złamałeś mój kod ...[1,1,2,2] - [1,2]
, otrzymasz pustą listę.[1,1,2,2] - [2]
daje[1,1]
Więc nie jest to tak naprawdę odejmowanie listy, jest bardziej jak „Lista z Listy X bez elementów z zestawu Y ” .y
doset
przed każdym czeku (który jest podobny do oryginalnego koszt pracy). Musisz zrobić toyset = set(y)
poza listcomp, a następnie przetestowaćif item not in yset
lub jako rażący hack,[item for yset in [set(y)] for item in x if item not in yset]
który nadużywa zagnieżdżonych listcomps, aby buforować jeyset
jako jednowierszowe . Nieco brzydkie jedno-liniowe rozwiązanie, które działa odpowiednio, byłoby użyteczne,list(itertools.filterfalse(set(y).__contains__, x))
ponieważ argument dofilterfalse
jest skonstruowany tylko raz.Użyj ustawionej różnicy
Lub możesz mieć po prostu xiy, więc nie musisz wykonywać żadnych konwersji.
źródło
TypeError: unhashable type: 'dict'
Jest to operacja „ustawiania odejmowania”. Użyj do tego ustawionej struktury danych.
W Python 2.7:
Wynik:
źródło
jeśli zduplikowane i zamawiane elementy stanowią problem:
[i for i in a if not i in b or b.remove(i)]
źródło
O(m * n)
środowisko uruchomieniowe (i wzdrygam się, ilekroć lista zawiera efekty uboczne); możesz to poprawić za pomocą,collections.Counter
aby uzyskaćO(m + n)
środowisko uruchomieniowe .W wielu przypadkach użycia odpowiedź brzmi:
Jest to hybryda między odpowiedzią aaronasterling a odpowiedzią quantumSoup .
Wersja aaronasterlinga
len(y)
porównuje elementy dla każdego elementux
, więc zajmuje kwadratowy czas. Wersja quantumSoup używa zestawów, więc wykonuje pojedyncze wyszukiwanie zestawu w czasie stałym dla każdego elementu wx
—Ale ponieważ konwertuje obax
iy
na zestawy, traci kolejność elementów.Konwertując tylko
y
na zbiór i iterującx
po kolei, otrzymujesz to, co najlepsze z obu światów - czas liniowy i zachowanie porządku. *Jednak nadal ma to problem z wersją quantumSoup: wymaga, aby twoje elementy mogły być haszowalne. Jest to dość wbudowane w naturę zbiorów. ** Jeśli próbujesz np. Odjąć listę nagrań z innej listy nagrań, ale lista do odjęcia jest duża, co robisz?
Jeśli potrafisz udekorować swoje wartości w sposób umożliwiający ich haszowanie, rozwiąże to problem. Na przykład za pomocą płaskiego słownika, którego wartości same są haszowalne:
Jeśli Twoje typy są nieco bardziej skomplikowane (np. Często masz do czynienia z wartościami kompatybilnymi z JSON, które są haszowalne, lub listami lub słownikami, których wartości są rekurencyjnie tego samego typu), możesz nadal korzystać z tego rozwiązania. Ale niektórych typów po prostu nie można przekształcić w nic, co da się mieszać.
Jeśli twoje przedmioty nie są i nie można ich utworzyć haszowalne, ale są porównywalne, możesz przynajmniej uzyskać log-liniowy czas (
O(N*log M)
który jest znacznie lepszy niżO(N*M)
czas rozwiązania listy, ale nie tak dobry jakO(N+M)
czas zbioru roztworem) do sortowania i przy użyciubisect
:Jeśli twoje przedmioty nie są haszowalne ani porównywalne, to utkniesz w kwadratowym rozwiązaniu.
* Pamiętaj, że możesz to również zrobić za pomocą pary
OrderedSet
obiektów, dla których możesz znaleźć przepisy i moduły innych firm. Ale myślę, że to jest prostsze.** Powodem wyszukiwania zestawów jest stały czas, ponieważ wszystko, co musi zrobić, to haszować wartość i sprawdzić, czy istnieje wpis dla tego skrótu. Jeśli nie może uzyskać wartości skrótu, to nie zadziała.
źródło
Wyszukiwanie wartości w zestawach jest szybsze niż wyszukiwanie ich na listach:
Uważam, że będzie to skalować nieco lepiej niż:
Oba zachowują kolejność list.
źródło
set(y)
i nie konwertowaćy
do nowego zestawu w każdej pętli? W przeciwnym razie bym odpowiedź potrzebę abarnert za:ys = set(y); [i for i in x if i not in ys]
.if i not in set(y)
zajmuje to 25% dłużej niżif i not in y
(gdziey
jest lista). Wstępna konwersja zestawu zajmuje 55% mniej czasu. Testowane z dość krótkimix
iy
, ale różnice powinny być bardziej wyraźne wraz z długością, jeśli w ogóle.y
dla każdego elementux
; chyba że porównanie równości jest naprawdę drogie w porównaniu do obliczenia skrótu, zawsze będzie to straconeitem not in y
.Jeśli listy pozwalają na duplikowanie elementów, możesz użyć Licznika z kolekcji:
Jeśli chcesz zachować kolejność elementów od x:
źródło
Counter.subtract
nie usuwa elementów o zerowej wartości (-
i-=
robi, ale nie robisubtract
), więc nigdy nie przestaniesz usuwać elementów. Którą chcesz zamienićnot v in c
znot c[v]
(która zwraca zero dla nieistniejących elementów, dzięki czemu można bezpiecznie testować powrót do „zeroiness” vianot
).Inne rozwiązania mają jeden z kilku problemów:
x = [1, 2, 2, 2]
iy = [2, 2]
konwertująy
na aset
, albo usuwają wszystkie pasujące elementy (pozostawiając[1]
tylko) lub usuwają jeden z każdego unikalnego elementu (pozostawiając[1, 2, 2]
), gdy właściwym zachowaniem byłoby usunięcie go2
dwukrotnie, pozostawiając[1, 2]
, lubO(m * n)
, gdzie optymalne rozwiązanie możeO(m + n)
działaćAlain był na dobrej drodze
Counter
do rozwiązania # 2 i # 3, ale to rozwiązanie straci porządek. Rozwiązaniem, które zachowuje porządek (usuwanie pierwszychn
kopii każdej wartości w celun
powtórzeń wlist
wartościach do usunięcia) jest:Wypróbuj online!
Aby usunąć ostatnie kopie każdego elementu, wystarczy zmienić
for
pętlę nafor val in reversed(x):
i dodaćout.reverse()
natychmiast po wyjściu zfor
pętli.Konstruowanie
Counter
jestO(n)
pod względemy
długości, iteracjax
jestO(n)
pod względemx
długości, aCounter
testy członkostwa i mutacje sąO(1)
, podczas gdy,list.append
amortyzowaneO(1)
(daneappend
mogą byćO(n)
, ale dla wieluappend
s, ogólnymi średnimi wielkimi O,O(1)
ponieważ coraz mniej z nich wymaga realokacji), więc cała wykonana praca jestO(m + n)
.Możesz również przetestować, aby ustalić, czy były w nim elementy
y
, które nie zostały usunięte zx
testowania:źródło
int
S w stałej długości tablicy) lub musi zrobić więcej, niżO(m + n)
pracy (np następną najlepszą Big -O byłoby posortowaćlist
unikalne pary wartość / liczba, zmieniającO(1)
dict
wyszukiwania na wyszukiwaniaO(log n)
binarne; potrzebne byłyby unikalne wartości wraz z ich liczbą, a nie tylko posortowane wartości nieunikalne, ponieważ w przeciwnym razie poniesienieO(n)
kosztów spowodowałoby usunięcie elementy z posortowanychlist
).Spróbuj tego.
źródło
Myślę, że najłatwiejszym sposobem na osiągnięcie tego jest użycie set ().
źródło
Odpowiedź udzielana przez @aaronasterling wygląda dobrze, jednak nie jest kompatybilny z interfejsem domyślnej listy:
x = MyList(1, 2, 3, 4)
vsx = MyList([1, 2, 3, 4])
. Zatem poniższy kod może być użyty jako bardziej przyjazna dla listy python:Przykład:
źródło
Myślę, że to jest szybsze:
źródło
Ten przykład odejmuje dwie listy:
źródło