it-swarm.com.de

Binäre Suche (Halbierung) in Python

Gibt es eine Bibliotheksfunktion, die eine binäre Suche in einer Liste/einem Tupel durchführt und die Position des Elements zurückgibt, wenn es gefunden wird, und 'False' (-1, Keine usw.), falls nicht?

Ich habe die Funktionen bisect_left/right im bisect-Modul gefunden, sie geben jedoch auch dann eine Position zurück, wenn das Element nicht in der Liste enthalten ist. Für die beabsichtigte Verwendung ist das vollkommen in Ordnung, aber ich möchte nur wissen, ob ein Element in der Liste enthalten ist oder nicht (ich möchte nichts einfügen).

Ich dachte daran, bisect_left zu verwenden und dann zu prüfen, ob das Element an dieser Position dem entspricht, was ich suche, aber das scheint umständlich zu sein (und ich muss auch Grenzen setzen, um zu prüfen, ob die Anzahl größer sein kann als die größte Zahl in meiner Liste) . Wenn es eine schönere Methode gibt, würde ich gerne darüber erfahren.

Edit Um zu klären, wofür ich das brauche: Ich bin mir bewusst, dass ein Wörterbuch dafür sehr gut geeignet wäre, aber ich versuche, den Speicherverbrauch so gering wie möglich zu halten. Meine beabsichtigte Verwendung wäre eine Art doppelter Nachschlagetabelle. Ich habe in der Tabelle eine Liste von Werten, und ich muss in der Lage sein, auf die Werte basierend auf ihrem Index zuzugreifen. Und ich möchte auch in der Lage sein, den Index eines bestimmten Werts oder None zu finden, wenn der Wert nicht in der Liste enthalten ist.

Die Verwendung eines Wörterbuchs wäre der schnellste Weg, würde jedoch (ungefähr) den Speicherbedarf verdoppeln.

Ich stellte diese Frage und dachte, ich hätte vielleicht etwas in den Python-Bibliotheken übersehen. Es scheint, dass ich meinen eigenen Code schreiben muss, wie Moe vorschlug.

162
rslite
from bisect import bisect_left

def binary_search(a, x, lo=0, hi=None):  # can't use a to specify default for hi
    hi = hi if hi is not None else len(a)  # hi defaults to len(a)   
    pos = bisect_left(a, x, lo, hi)  # find insertion position
    return (pos if pos != hi and a[pos] == x else -1)  # don't walk off the end
222
Dave Abrahams

Schauen Sie sich den Code für bisect_left/right an und passen Sie ihn an Ihren Zweck an.

so was:

def binary_search(a, x, lo=0, hi=None):
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        midval = a[mid]
        if midval < x:
            lo = mid+1
        Elif midval > x: 
            hi = mid
        else:
            return mid
    return -1
54
Moe

Dies ist ein wenig abseits (da Moes Antwort auf die Frage des OP vollständig erscheint), aber es lohnt sich vielleicht, die Komplexität für Ihr gesamtes Verfahren von Ende zu Ende zu betrachten. Wenn Sie etwas in einer sortierten Liste speichern (wo eine binäre Suche hilfreich wäre) und dann nur nach Existenz suchen, werden Sie dazu (Worst-Case, sofern nicht anders angegeben):

Sortierte Listen

  • O( n log n) to initially create the list (if it's unsorted data. O(n), if it's sorted )
  • O( log n) lookups (this is the binary search part)
  • O( n ) insert / delete (might be O(1) or O(log n) average case, depending on your pattern)

Mit einer set() sind Sie dabei

  • O (n) zu erstellen
  • O (1) Nachschlagen
  • O (1) Einfügen/Löschen

Was eine sortierte Liste wirklich bringt, sind Sie "next", "previous" und "range" (einschließlich Einfügen oder Löschen von Bereichen), dh O(1) oder O (| range |), wenn Sie einen Start beginnen Index. Wenn Sie diese Arten von Vorgängen nicht häufig verwenden, ist das Speichern als Gruppe und das Sortieren nach Anzeige insgesamt ein besseres Geschäft. set() verursacht sehr wenig zusätzlichen Aufwand in Python. 

37
Gregg Lind

Es kann erwähnenswert sein, dass die Bisect-Dokumente jetzt Suchbeispiele enthalten: http://docs.python.org/library/bisect.html#searching-sorted-lists

(Das Erhöhen von ValueError anstelle von -1 oder None ist mehr als Pythonic. List.index () macht es zum Beispiel. Aber natürlich können Sie die Beispiele an Ihre Bedürfnisse anpassen.)

12
Petr Viktorin

Am einfachsten ist es, bisect zu verwenden und eine Position zurück zu überprüfen, um zu sehen, ob der Artikel da ist:

def binary_search(a,x,lo=0,hi=-1):
    i = bisect(a,x,lo,hi)
    if i == 0:
        return -1
    Elif a[i-1] == x:
        return i-1
    else:
        return -1
11
Imran

Dies ist direkt aus dem Handbuch:

http://docs.python.org/2/library/bisect.html

8.5.1. Sortierte Listen durchsuchen

Die oben genannten bisect () - Funktionen sind nützlich, um Einfügepunkte zu finden, sie können jedoch für allgemeine Suchaufgaben schwierig oder unhandlich sein. Die folgenden fünf Funktionen zeigen, wie sie in die Standard-Lookups für sortierte Listen umgewandelt werden können:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError

Mit der geringfügigen Änderung sollte Ihr Code also sein:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    return -1
7
arainchi

Ich stimme zu, dass die Antwort von @ DaveAbrahams die Verwendung des Bisect-Moduls der richtige Ansatz ist. In seiner Antwort erwähnte er kein wichtiges Detail.

Aus den docsbisect.bisect_left(a, x, lo=0, hi=len(a))

Das Halbierungsmodul erfordert nicht, dass das Suchfeld vorab vorausberechnet wird. Sie können die Endpunkte dem bisect.bisect_left einfach mit den Standardwerten von 0 und len(a) präsentieren.

Noch wichtiger für meine Verwendung ist es, nach einem Wert X zu suchen, bei dem der Fehler einer bestimmten Funktion minimiert wird. Dafür brauchte ich einen Weg, den Algorithmus von bisect_left stattdessen meine Berechnung aufzurufen. Das ist sehr einfach.

Geben Sie einfach ein Objekt an, das __getitem__ als a definiert.

Zum Beispiel könnten wir den Bisect-Algorithmus verwenden, um eine Quadratwurzel mit beliebiger Genauigkeit zu finden!

import bisect

class sqrt_array(object):
    def __init__(self, digits):
        self.precision = float(10**(digits))
    def __getitem__(self, key):
        return (key/self.precision)**2.0

sa = sqrt_array(4)

# "search" in the range of 0 to 10 with a "precision" of 0.0001
index = bisect.bisect_left(sa, 7, 0, 10*10**4)
print 7**0.5
print index/(10**4.0)
6
paulluap

Wenn Sie nur sehen möchten, ob es vorhanden ist, versuchen Sie, die Liste in ein Diktat umzuwandeln:

# Generate a list
l = [n*n for n in range(1000)]

# Convert to dict - doesn't matter what you map values to
d = dict((x, 1) for x in l)

count = 0
for n in range(1000000):
    # Compare with "if n in l"
    if n in d:
        count += 1

Auf meinem Rechner dauerte "if n in l" 37 Sekunden, während "if n in d" 0,4 Sekunden dauerte.

4
jrb

Die Lösung von Dave Abrahams ist gut. Obwohl ich es minimalistisch gemacht hätte:

def binary_search(L, x):
    i = bisect.bisect_left(L, x)
    if i == len(L) or L[i] != x:
        return -1
    return i
2
Florent

Dieser ist:

  • nicht rekursiv (was speichereffizient als die meisten rekursiven Ansätze macht)
  • eigentlich Arbeit
  • schnell, da es läuft ohne unnötige if's und Bedingungen
  • basierend auf einer mathematischen Behauptung dass der Boden von (niedrig + hoch)/2 ist immer kleiner als hoch wobei niedrig die untere Grenze ist und hoch ist die Obergrenze.
  • getestet: D

def binsearch(t, key, low = 0, high = len(t) - 1):
    # bisecting the range
    while low < high:
        mid = (low + high)//2
        if t[mid] < key:
            low = mid + 1
        else:
            high = mid
    # at this point 'low' should point at the place
    # where the value of 'key' is possibly stored.
    return low if t[low] == key else -1
2

Zwar gibt es in Python keinen expliziten binären Suchalgorithmus, aber es gibt ein Modul - bisect -, das die Einfügemarke für ein Element in einer sortierten Liste mithilfe einer binären Suche ermittelt. Dies kann zur Durchführung einer binären Suche "ausgetrickst" werden. Der größte Vorteil ist derselbe Vorteil, den auch der Bibliothekscode bietet - er ist leistungsstark, gut getestet und funktioniert einfach (insbesondere binäre Suchvorgänge sind ziemlich schwer zu implementieren ) - insbesondere wenn Edge-Fälle nicht sorgfältig sind berücksichtigt).

Grundtypen

Für Basistypen wie Strings oder Ints ist es ziemlich einfach - Sie benötigen lediglich das Modul bisect und eine sortierte Liste:

>>> import bisect
>>> names = ['bender', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> bisect.bisect_left(names, 'fry')
1
>>> keyword = 'fry'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
True
>>> keyword = 'arnie'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
False

Sie können dies auch verwenden, um Duplikate zu finden:

...
>>> names = ['bender', 'fry', 'fry', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> keyword = 'fry'
>>> leftIndex = bisect.bisect_left(names, keyword)
>>> rightIndex = bisect.bisect_right(names, keyword)
>>> names[leftIndex:rightIndex]
['fry', 'fry', 'fry']

Natürlich können Sie bei Bedarf einfach den Index zurückgeben und nicht den Wert an diesem Index.

Objekte

Bei benutzerdefinierten Typen oder Objekten sind die Dinge etwas komplizierter: Sie müssen unbedingt umfangreiche Vergleichsmethoden implementieren, damit Bisect korrekt verglichen wird.

>>> import bisect
>>> class Tag(object):  # a simple wrapper around strings
...     def __init__(self, tag):
...         self.tag = tag
...     def __lt__(self, other):
...         return self.tag < other.tag
...     def __gt__(self, other):
...         return self.tag > other.tag
...
>>> tags = [Tag('bender'), Tag('fry'), Tag('leela'), Tag('nibbler'), Tag('zoidbe
rg')]
>>> key = Tag('fry')
>>> leftIndex = bisect.bisect_left(tags, key)
>>> rightIndex = bisect.bisect_right(tags, key)
>>> print([tag.tag for tag in tags[leftIndex:rightIndex]])
['fry']

Dies sollte zumindest in Python 2.7 -> 3.3 funktionieren

2
stephenfin

Dieser Code funktioniert rekursiv mit Integer-Listen. Sucht nach dem einfachsten Szenario, das heißt: Listenlänge kleiner als 2. Dies bedeutet, dass die Antwort bereits vorhanden ist, und es wird ein Test durchgeführt, um die korrekte Antwort zu überprüfen Die korrekte, wenn nicht Halbierung erfolgt durch erneutes Aufrufen der Funktion, wobei der mittlere Wert als obere oder untere Grenze festgelegt wird, indem er nach links oder rechts verschoben wird

 def binary_search (intList, intValue, lowValue, highValue): 
 if (highValue - lowValue) & lt 2: 
 return intList [lowValue] == intValue oder intList [highValue] == intValue 
 middleValue = lowValue + ((highValue - lowValue)/2) 
 wenn intList [middleValue] == intValue: 
 return True 
 wenn intList [middleValue]> intValue: 
 return binary_search (intList, intValue, lowValue, middleValue - 1) 
 return binary_search (intList, intValue, middleValue + 1, highValue) 
1
rct

Die Verwendung eines Diktats würde die Speicherverwendung nicht verdoppeln, wenn die von Ihnen gespeicherten Objekte wirklich klein sind, da die Werte nur Zeiger auf die tatsächlichen Objekte sind:

>>> a = 'foo'
>>> b = [a]
>>> c = [a]
>>> b[0] is c[0]
True

In diesem Beispiel wird 'foo' nur einmal gespeichert. Macht das einen Unterschied für Sie? Und über wie viele Dinge sprechen wir überhaupt?

1
Kirk Strauser

Beispiele in Wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm

def binary_search(a, key, imin=0, imax=None):
    if imax is None:
        # if max amount not set, get the total
        imax = len(a) - 1

    while imin <= imax:
        # calculate the midpoint
        mid = (imin + imax)//2
        midval = a[mid]

        # determine which subarray to search
        if midval < key:
            # change min index to search upper subarray
            imin = mid + 1
        Elif midval > key:
            # change max index to search lower subarray
            imax = mid - 1
        else:
            # return index number 
            return mid
    raise ValueError
1
jdsantiagojr
  • s ist eine Liste. 
  • binary(s, 0, len(s) - 1, find) ist der erste Anruf.
  • Funktion gibt einen Index des abgefragten Elements zurück. Wenn kein solcher Artikel vorhanden ist, wird -1 zurückgegeben.

    def binary(s,p,q,find):
        if find==s[(p+q)/2]:
            return (p+q)/2
        Elif p==q-1 or p==q:
            if find==s[q]:
                return q
            else:
                return -1
        Elif find < s[(p+q)/2]:
            return binary(s,p,(p+q)/2,find)
        Elif find > s[(p+q)/2]:
            return binary(s,(p+q)/2+1,q,find)
    
0
AV94

Ich brauchte die binäre Suche in Python und generisch für Django-Modelle. In Django-Modellen kann ein Modell einen Fremdschlüssel zu einem anderen Modell haben, und ich wollte die abgerufenen Modellobjekte durchsuchen. Ich habe folgende Funktion geschrieben, die Sie verwenden können.

def binary_search(values, key, lo=0, hi=None, length=None, cmp=None):
    """
    This is a binary search function which search for given key in values.
    This is very generic since values and key can be of different type.
    If they are of different type then caller must specify `cmp` function to
    perform a comparison between key and values' item.
    :param values:  List of items in which key has to be search
    :param key: search key
    :param lo: start index to begin search
    :param hi: end index where search will be performed
    :param length: length of values
    :param cmp: a comparator function which can be used to compare key and values
    :return: -1 if key is not found else index
    """
    assert type(values[0]) == type(key) or cmp, "can't be compared"
    assert not (hi and length), "`hi`, `length` both can't be specified at the same time"

    lo = lo
    if not lo:
        lo = 0
    if hi:
        hi = hi
    Elif length:
        hi = length - 1
    else:
        hi = len(values) - 1

    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if not cmp:
            if values[mid] == key:
                return mid
            if values[mid] < key:
                lo = mid + 1
            else:
                hi = mid - 1
        else:
            val = cmp(values[mid], key)
            # 0 -> a == b
            # > 0 -> a > b
            # < 0 -> a < b
            if val == 0:
                return mid
            if val < 0:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1
0
sonus21

Viele gute Lösungen oben, aber ich habe keine einfache (KISS halte es einfach (weil ich bin)) dumme Verwendung der eingebauten/generischen Bisect-Funktion von Python für eine binäre Suche verwendet. Mit etwas Code um die Bisect-Funktion Ich denke, ich habe unten ein Beispiel, in dem ich alle Fälle auf ein kleines String-Array von Namen getestet habe: Einige der oben genannten Lösungen deuten darauf hin, dass der folgende Code hoffentlich jedem, der so verwirrt ist wie ich, hilft. 

Python-Halbschnitt wird verwendet, um anzugeben, wo ein neuer Wert/Sucheintrag in eine sortierte Liste eingefügt werden soll. Der nachstehende Code, der bisect_left verwendet, gibt den Index des Treffers zurück, wenn der Suchbegriff in der Liste/im Array gefunden wird (Hinweis bisect und bisect_right geben den Index des Elements nach dem Treffer oder der Übereinstimmung als Einfügepunkt zurück.) Falls nicht gefunden , bisect_left gibt einen Index zum nächsten Element in der sortierten Liste zurück, der nicht den Suchwert == wird. Der einzige andere Fall ist der Fall, bei dem das Suchelement am Ende der Liste angezeigt wird, an dem der zurückgegebene Index über das Ende der Liste/des Arrays hinausgeht und der sich im Code unter dem frühen Beenden von Python mit den Logikhandles "und" befindet. (Erste Bedingung False Python prüft keine nachfolgenden Bedingungen)

#Code
from bisect import bisect_left
names=["Adam","Donny","Jalan","Zach","Zayed"]
search=""
lenNames = len(names)
while search !="none":
    search =input("Enter name to search for or 'none' to terminate program:")
    if search == "none":
        break
    i = bisect_left(names,search)
    print(i) # show index returned by Python bisect_left
    if i < (lenNames) and names[i] == search:
        print(names[i],"found") #return True - if function
    else:
        print(search,"not found") #return False – if function
##Exhaustive test cases:
##Enter name to search for or 'none' to terminate program:Zayed
##4
##Zayed found
##Enter name to search for or 'none' to terminate program:Zach
##3
##Zach found
##Enter name to search for or 'none' to terminate program:Jalan
##2
##Jalan found
##Enter name to search for or 'none' to terminate program:Donny
##1
##Donny found
##Enter name to search for or 'none' to terminate program:Adam
##0
##Adam found
##Enter name to search for or 'none' to terminate program:Abie
##0
##Abie not found
##Enter name to search for or 'none' to terminate program:Carla
##1
##Carla not found
##Enter name to search for or 'none' to terminate program:Ed
##2
##Ed not found
##Enter name to search for or 'none' to terminate program:Roger
##3
##Roger not found
##Enter name to search for or 'none' to terminate program:Zap
##4
##Zap not found
##Enter name to search for or 'none' to terminate program:Zyss
##5
##Zyss not found
0
Bob

Binäre Suche :  

// List - values inside list
// searchItem - Item to search
// size - Size of list
// upperBound - higher index of list
// lowerBound - lower index of list
def binarySearch(list, searchItem, size, upperBound, lowerBound):
        print(list)
        print(upperBound)
        print(lowerBound)
        mid = ((upperBound + lowerBound)) // 2
        print(mid)
        if int(list[int(mid)]) == value:
               return "value exist"
        Elif int(list[int(mid)]) < value:
             return searchItem(list, value, size, upperBound, mid + 1)
        Elif int(list[int(mid)]) > value:
               return searchItem(list, value, size, mid - 1, lowerBound)

// Um ​​die Funktion oben aufzurufen, verwenden Sie:

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
searchItem = 1        
print(searchItem(list[0], item, len(list[0]) -1, len(list[0]) - 1, 0))
0
jitesh mohite
'''
Only used if set your position as global
'''
position #set global 

def bst(array,taget): # just pass the array and target
        global position
        low = 0
        high = len(array)
    while low <= high:
        mid = (lo+hi)//2
        if a[mid] == target:
            position = mid
            return -1
        Elif a[mid] < target: 
            high = mid+1
        else:
            low = mid-1
    return -1

Ich denke, das ist viel besser und effektiver. bitte korrigiere mich :). Vielen Dank

0
iraycd
def binary_search_length_of_a_list(single_method_list):
    index = 0
    first = 0
    last = 1

    while True:
        mid = ((first + last) // 2)
        if not single_method_list.get(index):
            break
        index = mid + 1
        first = index
        last = index + 1
    return mid
0
user3412550