it-swarm.com.de

Schneller als binäre Suche nach geordneter Liste

gibt es einen Algorithmus, der schneller als die binäre Suche ist, um in sortierten Werten eines Arrays zu suchen?

in meinem Fall habe ich einen sortierten Wert (kann ein beliebiger Typ sein) in einem A-Array. Ich muss n zurückgeben, wenn der gesuchte Wert im Bereich von A[n] and A[n+1] liegt.

27
uray

Sie können eine bessere Leistung als O (log n) erzielen, wenn die Werte Ganzzahlen sind. In diesem Fall ist die beste Laufzeit im ungünstigsten Fall, die Sie in Bezug auf n erzielen können, O (sqrt (log n)). Andernfalls besteht keine Möglichkeit, O (log n) zu schlagen, es sei denn, die Eingabesequenz enthält Muster. Es gibt zwei Ansätze, um bei ganzen Zahlen O (log n) zu schlagen.

Erstens können Sie y-schnelle Bäume verwenden, indem Sie in einer Hash-Tabelle alle Präfixe speichern, für die Sie mindestens eine Ganzzahl mit diesem Präfix speichern. Auf diese Weise können Sie eine binäre Suche durchführen, um die Länge des am längsten übereinstimmenden Präfixes zu ermitteln. Auf diese Weise können Sie den Nachfolger eines Elements finden, nach dem Sie in der Zeit O (log w) suchen, wobei w die Anzahl der Bits in einem Wort ist. Es gibt zwar einige Details, die Sie bearbeiten müssen, damit dies funktioniert und nur linearen Raum verwendet. Sie sind jedoch nicht allzu schlecht (siehe den Link unten).

Zweitens können Sie Fusionsbäume verwenden, die mithilfe von Bit-Tricks w ^ O (1) -Vergleiche in nur einer konstanten Anzahl von Befehlen durchführen und eine Laufzeit von O (log n/log w) ergeben.

Der optimale Kompromiss zwischen diesen beiden Datenstrukturen tritt auf, wenn log w = sqrt (log n), was eine Laufzeit von O (sqrt (log n)) ergibt.

Einzelheiten hierzu finden Sie in den Vorlesungen 12 und 13 des Kurses von Erik Demaine: http://courses.csail.mit.edu/6.851/spring07/lec.html

35
jonderry

Eine Möglichkeit besteht darin, es so zu behandeln, als ob man die Wurzeln einer Funktion findet. Grundsätzlich finden:

a[i] <= i <= a[i + 1]

Ist äquivalent zu:

a[i] - i <= 0 <= a[i + 1] - i

Dann könnten Sie so etwas wie Newtons Methode ausprobieren und so weiter. Diese Art von Algorithmen konvergieren häufig schneller als eine binäre Suche, wenn sie funktionieren, aber ich kenne keinen, der für alle Eingaben garantiert konvergiert.

http://en.wikipedia.org/wiki/Root-finding_algorithm

6
xscott

Wenn die Werte in der Liste gleichmäßig verteilt sind, können Sie eine gewichtete Aufteilung anstelle einer binären Aufteilung versuchen, z. Wenn der gewünschte Wert ein Drittel von der aktuellen Untergrenze bis zum aktuellen Wert beträgt, können Sie das Element ausprobieren, bei dem es sich auch um ein Drittel handelt. Dies kann jedoch auf Listen, in denen die Werte zusammengefasst sind, zu schweren Problemen führen.

Ja und nein. Ja, es gibt Suchvorgänge, die im Durchschnitt schneller sind als eine Halbierungssuche. Aber ich glaube, dass sie immer noch O (lg N) sind, nur mit einer niedrigeren Konstante.

Sie möchten den Zeitaufwand für das Auffinden Ihres Elements minimieren. Im Allgemeinen ist es wünschenswert, weniger Schritte zu verwenden, und eine Möglichkeit, dies zu erreichen, besteht darin, die erwartete Anzahl von Elementen zu maximieren, die bei jedem Schritt eliminiert werden. Bei der Halbierung wird immer genau die Hälfte der Elemente eliminiert. Sie können es besser machen, wenn Sie etwas über die Verteilung der Elemente wissen. Der Algorithmus zur Auswahl des Partitionselements ist jedoch im Allgemeinen komplizierter als die Auswahl des Mittelpunkts, und diese zusätzliche Komplexität kann jede Zeitersparnis überfordern, die Sie durch die Verwendung weniger Schritte erwarten.

In einem solchen Problem ist es wirklich besser, Effekte zweiter Ordnung wie die Cache-Lokalität anzugreifen als den Suchalgorithmus. Wenn Sie beispielsweise eine wiederholte binäre Suche durchführen, werden dieselben wenigen Elemente (erstes, zweites und drittes Quartil) SEHR häufig verwendet, sodass das Einfügen in eine einzelne Cache-Zeile dem wahlfreien Zugriff auf die Liste weit überlegen sein kann.

Das Teilen jeder Ebene in beispielsweise 4 oder 8 gleiche Abschnitte (anstelle von 2) und das Durchführen einer linearen Suche durch diese Abschnitte könnte auch schneller als die Halbierungssuche sein, da für eine lineare Suche keine Berechnung der Partition erforderlich ist und außerdem weniger Datenabhängigkeiten möglich sind Cachestände verursachen.

Aber alle diese sind immer noch O (lg N).

4
Ben Voigt

Was ist mit dem folgenden Algo? Es heißt Exponential Search und ist eine der Variationen der binären Suche. http://en.m.wikipedia.org/wiki/Exponential_search

Suche nach Element k im sortierten Array A der Größe n. Suchen Sie nach A [2 ^ i] für i = 0, 1, 2, ..., bis Sie über ks Position in A hinausgehen. Führen Sie dann eine binäre Suche für den Teil des Arrays durch, der links (kleiner) als i liegt.

int exponential_search(int A[], int key)
{
  // lower and upper bound for binary search
  int lower_bound = 0;
  int upper_bound = 1;

  // calculate lower and upper bound
  while (A[upper_bound] < key) {
    lower_bound = upper_bound;
   upper_bound = upper_bound * 2;
  }
  return binary_search(A, key, lower_bound, upper_bound);
}

Dieser Algorithmus wird auf O (log idx) ausgeführt, wobei idx der Index von k in A ist (beide stpes sind in log idx). Im schlimmsten Fall ist das Algo in O (log idx), wenn k zu den größten Elementen von A gehört oder größer als jedes Element von A. Die multiplikative Konstante ist größer als bei der binären Suche, aber das Algo würde bei sehr großen Elementen schneller laufen Arrays und bei der Suche nach Daten, die sich am Anfang des Arrays befinden.

Ich hätte gerne eine Vorstellung von der minimalen Größe n, bei der dieses Algo der binären Suche vorzuziehen ist, aber ich weiß es nicht.

3
user2747438

Zunächst Measure vor der Optimierung.

Müssen Sie diese Suche wirklich optimieren?

Wenn ja, dann denken Sie zweitens zuerst an die algorithmische Komplexität. Z.B. können Sie einen Baum (wie zum Beispiel einen std::map) anstelle eines Arrays verwenden? In diesem Fall hängt es von der relativen Häufigkeit von Einfügungen/Löschungen im Vergleich zu Suchvorgängen ab. Die Prämisse, dass ein sortiertes Array zur Verfügung steht, zeigt jedoch, dass Suchvorgänge im Vergleich zu Datensatzänderungen häufig sind Einfügungen/Löschungen beschleunigen die Suche erheblich - und zwar in logarithmischer Zeit.

Wenn Sie feststellen, dass die Suchzeiten tatsächlich einen Engpass darstellen, der adressiert werden muss, und nein, keine Änderung der Datendarstellung möglich ist und die Liste kurz ist, ist eine lineare Suche im Allgemeinen schneller, da sie pro Vergleich weniger Arbeit leistet.

Andernfalls, wenn die Liste länger ist und keine bestimmte Werteverteilung bekannt ist oder angenommen wird und die Werte nicht als numerisch behandelt werden können und der Speicherverbrauch konstant sein sollte (z. B. das Erstellen einer Hash-Tabelle ist ausgeschlossen), erfolgt die binäre Suche Erzeugt 1 Bit Information pro Vergleich und ist wahrscheinlich das Beste, was Sie für die erste Suche tun können.

Prost & Hth.

Sie können sie immer in eine Hash-Tabelle einfügen, dann lautet die Suche O (1). Es ist jedoch speicherintensiv, und wenn Sie weiterhin Elemente hinzufügen, muss die Hash-Tabelle möglicherweise neu erstellt werden. Re-Bucketing ist O(n), wird jedoch auf O (1) abgeschrieben. Dies hängt im Wesentlichen davon ab, ob Sie sich diesen Platz leisten können und ob der Cache möglicherweise nicht ausreicht.

1
srean

Wenn Sie eine große Anzahl von Zahlen zu finden haben und diese nach Zufall auch sortiert sind, können Sie dies in O (n + m) tun, wobei m die Anzahl der zu findenden Zahlen ist. Grundsätzlich nur ein typischer Merge-Algorithmus, mit geringfügiger Änderung, um aufzuzeichnen, welcher Wert vor jeder überprüften Zahl eingefügt werden würde, wenn sie in das Array eingefügt werden sollte.

Sie können jederzeit den Raum und die Zeit anderer Operationen außer Acht lassen. Angenommen, alle Ihre Elemente haben eine konstante Größe von p Bits, können Sie ein massives Array erstellen, das für jeden möglichen Wert, den Sie nachschlagen könnten, den Index des nächstgrößeren Werts speichert, der derzeit gespeichert ist. Dieses Array muss aus 2 ^ p * lg (n) Bits bestehen, wobei n die gespeicherten Zahlenwerte sind. Jede Einfügung oder Löschung ist O (2 ^ p), aber normalerweise ungefähr 2 ^ p/n, da Sie alle diese Indizes aktualisieren müssen.

Aber dein Lookup ist jetzt O (1)!

OK, OK, das ist nicht wirklich praktisch. Eine ähnliche Aufteilung der Eingabe in Blöcke kann jedoch möglicherweise die Konstante vor Ihrem Protokoll verringern. Möglicherweise.

0
David

Obwohl Sie im Allgemeinen nicht besser als O (log N) abschneiden können, können Sie dies zumindest optimieren und so die Proportionalitätskonstante vor O (log N) erheblich reduzieren.

Wenn Sie mehrere Suchvorgänge für dasselbe Array ausführen müssen, können diese mithilfe von SIMD-Erweiterungen vektorisiert werden, wodurch die Berechnungskosten weiter gesenkt werden.

Insbesondere wenn es sich um Arrays von Gleitkommazahlen handelt, die bestimmte Eigenschaften erfüllen, gibt es Möglichkeiten, einen speziellen Index zu erstellen, mit dem das Array in O (1) durchsucht werden kann.

Alle oben genannten Aspekte werden mit Testergebnissen diskutiert in: Cannizzo, 2015, Schnelle und vektorisierbare Alternative zur binären Suche in O(1) Anwendbar auf einen weiten Bereich sortierter Arrays von Gleitkommazahlen Das Papier kommt mit Quellcode auf Github .

0
Fabio

Bei der binären Suche teilen Sie die Liste in zwei "Unterlisten" auf und durchsuchen nur die Unterliste, die den Wert enthalten darf. Je nachdem, wie groß Ihr Array ist, können Sie eine Beschleunigung feststellen, wenn Sie das Array in mehr als zwei Spleiße aufteilen.

Sie können bestimmen, welchen Bereich des Arrays Sie durchsuchen müssen, indem Sie einen Index führen, den Sie zuerst durchsuchen. Wie in einem Telefonbuch einer Großstadt, in dem man von außen sieht, wo man anfangen muss zu suchen. (Ich habe Probleme, meine Idee im Text auszudrücken, und ich habe noch keinen englischen Link gefunden, der es besser erklärt.).

0
bjoernz