2016-06-03 11 views
3

Sollte beim Umgang mit Listen/Tupeln von Floats die Verwendung von in oder nicht in vermieden werden? Ist die Implementierung etwas wie der Code unten oder ist es etwas ausgeklügelter?"in" -Anweisung für Listen/Tupel von Floats

check = False 
for item in list_to_search_the_value_in: 
    if value_to_search_for == item: 
     check = True 
+0

Vielleicht möchten Sie [hier] (http://stackoverflow.com/questions/2217001/override-in-operator-in-python) sehen. Der 'in'-Operator sollte bevorzugt werden, da er einen speziellen Containment-Test verwenden kann, der vom Container angeboten wird (z.B.' set .__ contains __() 'ist viel schneller als' list .__ contains __() '). Das Problem ist mit dem "float" -Teil, weil das Vergleichen von Floats aus verschiedenen Quellen für Gleichheit normalerweise ein numerisches No-Go ist. – dhke

+0

Ja. Vergleichen von Floats für Gleichheit ist am besten zu vermeiden, für die [üblichen Gründe] (http://stackoverflow.com/questions/588004/is-floating-point-math-broken?rq=1). –

+0

Eine mögliche Alternative besteht darin, die Liste der Gleitkommazahlen zu sortieren und die binäre Suche zu verwenden, um die beste Übereinstimmung zu finden, zu subtrahieren und zu prüfen, ob die Differenz unter einem bestimmten Grenzwert liegt. –

Antwort

3

in und not in sollten Sie Ihre bevorzugte Art der Mitgliedschaft Prüfung sein. Beide Operatoren können (über __contains__()) jeden optimierten Mitgliedschaftstest verwenden, den der Container anbietet.

Ihr Problem ist mit dem float Teil, weil in einen Gleichheitsvergleich mit == macht (optimiert, um Identität zuerst zu prüfen).

Im Allgemeinen führt der Vergleich von Gleitkommazahlen zur Gleichheit nicht zu den gewünschten Ergebnissen. Daher für Listen von Schwimmern, wollen Sie so etwas wie

def is_in_float(item, sequence, eps=None): 
    eps = eps or 2**-52 
    return any((abs(item - seq_item) < eps) for seq_item in sequence) 

Verwendung mit Sortier- und binäre Suche den nächsten passenden Schwimmer an Ihrer Bequemlichkeit zu finden.

2

Here's der Teil der Dokumentation sagt, dass in prüft Arten Gleichheit auf Folge. Also nein, das sollte nicht für Sequenzen von Floats verwendet werden.

1

Der Operator in verwendet normale Gleichheitsprüfungen hinter den Kulissen, so dass es die gleichen Einschränkungen wie __eq__() hat, wenn es um Floats geht. Verwenden Sie mit Vorsicht, wenn überhaupt.

>>> 0.3 == 0.4 - 0.1 
False 

>>> 0.3 in [0.4 - 0.1] 
False 
1

Seit in Operator verwendet Gleichheitsprüfung, wird es häufig fehlschlagen, da floating point math is "broken" (naja, es ist nicht, aber Sie bekommen einen Punkt).

Sie können leicht eine ähnliche Funktionalität erreichen durch any mit:

epsilon = 1e-9 

check = any(abs(f - value_to_search_for) < epsilon for f in seq) 
# or 
check = False 
if any(abs(f - value_to_search_for) < epsilon for f in seq): 
    check = True 
1

list Typ Python hat seine __contains__ Methode implemented in C:

static int 
list_contains(PyListObject *a, PyObject *el) 
{ 
    Py_ssize_t i; 
    int cmp; 

    for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i) 
     cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i), 
              Py_EQ); 
    return cmp; 
} 

Eine wörtliche Übersetzung Python sein könnte:

def list_contains(a, el): 
    cmp = False 
    for i in range(len(a)): 
     if cmp: break 
     cmp = a[i] == el 
    return cmp 

Ihr Beispiel ist eine idiomatische Übersetzung.

In jedem Fall, wie die anderen Antworten bemerkt haben, verwendet es Gleichheit, um die Listenelemente gegen das Element zu testen, das Sie auf Mitgliedschaft überprüfen. Mit float Werten, die gefährlich sein können, da Zahlen, die wir erwarten, gleich zu sein, möglicherweise nicht auf Gleitkomma-Rundung zurückzuführen sind.

Eine float -safe Art und Weise von der selbst sein könnte, überprüfen Umsetzung:

any(abs(x - el) < epsilon for x in a) 

wo epsilon etwas kleiner Wert ist. Wie klein es sein muss, hängt von der Größe der Zahlen ab, mit denen Sie es zu tun haben, und davon, wie genau Sie sich interessieren.Wenn Sie den Betrag des numerischen Fehlers schätzen können, der el einen äquivalenten Wert in der Liste unterscheiden könnte, können Sie epsilon um eine Größenordnung größer einstellen und sicher sein, dass Sie kein falsches Negativ geben (und wahrscheinlich nur falsche positive Ergebnisse geben) Fälle, die unmöglich zu bekommen sind).

Verwandte Themen