In meiner Freizeit habe ich in letzter Zeit verschiedene Algorithmen kennengelernt. Einer, auf den ich gestoßen bin und der sehr interessant erscheint, heißt HyperLogLog-Algorithmus. Er schätzt, wie viele eindeutige Elemente in einer Liste enthalten sind.
Dies war besonders interessant für mich, weil es mich zu meinen MySQL-Tagen zurückbrachte, als ich diesen "Kardinalitäts" -Wert sah (von dem ich bis vor kurzem immer angenommen hatte, dass er nicht geschätzt berechnet wurde).
Ich weiß also, wie man einen Algorithmus in O ( n ) schreibt , der berechnet, wie viele eindeutige Elemente sich in einem Array befinden. Ich habe das in JavaScript geschrieben:
function countUniqueAlgo1(arr) {
var Table = {};
var numUnique = 0;
var numDataPoints = arr.length;
for (var j = 0; j < numDataPoints; j++) {
var val = arr[j];
if (Table[val] != null) {
continue;
}
Table[val] = 1;
numUnique++;
}
return numUnique;
}
Das Problem ist jedoch, dass mein Algorithmus, während O ( n ), viel Speicher benötigt (Speichern von Werten in Table
).
Ich habe dieses Papier darüber gelesen, wie man Duplikate in einer Liste in O ( n ) Zeit zählt und nur minimalen Speicher benötigt.
Es wird erklärt, dass durch Hashing und Zählen von Bits oder Ähnlichem die Anzahl der eindeutigen Elemente in einer Liste innerhalb einer bestimmten Wahrscheinlichkeit (unter der Annahme, dass die Liste gleichmäßig verteilt ist) geschätzt werden kann.
Ich habe die Zeitung gelesen, kann sie aber nicht verstehen. Kann jemand die Erklärung eines Laien geben? Ich weiß, was Hashes sind, aber ich verstehe nicht, wie sie in diesem HyperLogLog-Algorithmus verwendet werden.
Antworten:
Der Haupttrick hinter diesem Algorithmus besteht darin, dass, wenn Sie einen Strom zufälliger Ganzzahlen beobachten und eine Ganzzahl sehen, deren binäre Darstellung mit einem bekannten Präfix beginnt, die Wahrscheinlichkeit höher ist, dass die Kardinalität des Stroms 2 ^ beträgt (Größe des Präfixes). .
Das heißt, in einem zufälligen Strom von ganzen Zahlen beginnen ~ 50% der Zahlen (binär) mit "1", 25% mit "01", 12,5% mit "001". Dies bedeutet, dass, wenn Sie einen zufälligen Stream beobachten und eine "001" sehen, die Wahrscheinlichkeit höher ist, dass dieser Stream eine Kardinalität von 8 hat.
(Das Präfix "00..1" hat keine besondere Bedeutung. Es ist nur deshalb vorhanden, weil es bei den meisten Prozessoren leicht ist, das höchstwertige Bit in einer Binärzahl zu finden.)
Wenn Sie nur eine ganze Zahl beobachten, ist die Wahrscheinlichkeit, dass dieser Wert falsch ist, natürlich hoch. Aus diesem Grund unterteilt der Algorithmus den Stream in "m" unabhängige Teilströme und behält die maximale Länge eines sichtbaren "00 ... 1" -Präfixes jedes Teilstroms bei. Schätzt dann den Endwert, indem der Mittelwert jedes Teilstroms genommen wird.
Das ist die Hauptidee dieses Algorithmus. Es fehlen einige Details (z. B. die Korrektur für niedrige Schätzwerte), aber in der Arbeit ist alles gut geschrieben. Entschuldigung für das schreckliche Englisch.
quelle
Ein HyperLogLog ist eine probabilistische Datenstruktur . Es zählt die Anzahl der verschiedenen Elemente in einer Liste. Aber im Vergleich zu einer einfachen Methode (eine Menge zu haben und Elemente zur Menge hinzuzufügen) geschieht dies ungefähr.
Bevor Sie sich ansehen, wie der HyperLogLog-Algorithmus dies tut, müssen Sie verstehen, warum Sie ihn benötigen. Das Problem mit einem einfachen Weg ist, dass es Platz verbraucht
O(distinct elements)
. Warum gibt es hier eine große O-Notation statt nur bestimmter Elemente? Dies liegt daran, dass Elemente unterschiedliche Größen haben können. Ein Element kann ein1
anderes Element sein"is this big string"
. Wenn Sie also eine große Liste (oder einen großen Strom von Elementen) haben, wird viel Speicherplatz benötigt.Probabilistisches Zählen
Wie kann man eine vernünftige Schätzung einer Reihe einzigartiger Elemente erhalten? Angenommen, Sie haben eine Zeichenfolge mit einer Länge,
m
die{0, 1}
mit gleicher Wahrscheinlichkeit besteht. Wie groß ist die Wahrscheinlichkeit, dass es mit 0, mit 2 Nullen, mit k Nullen beginnt? Es ist1/2
,1/4
und1/2^k
. Dies bedeutet, dass Sie, wenn Sie auf eine Zeichenfolge mitk
Nullen gestoßen sind, ungefähr2^k
Elemente durchgesehen haben . Das ist also ein guter Ausgangspunkt. Wenn Sie eine Liste von Elementen haben, die gleichmäßig verteilt sind,0
und2^k - 1
Sie die maximale Anzahl des größten Präfixes von Nullen in der Binärdarstellung zählen können, erhalten Sie eine vernünftige Schätzung.Das Problem ist, dass die Annahme, gleichmäßig verteilte Zahlen von
0
t zu haben,2^k-1
zu schwer zu erreichen ist (die Daten, auf die wir gestoßen sind, sind meist keine Zahlen, fast nie gleichmäßig verteilt und können zwischen beliebigen Werten liegen. Mit einer guten Hashing-Funktion können Sie dies jedoch annehmen Die Ausgangsbits wären gleichmäßig verteilt und die meisten Hashing-Funktionen hätten Ausgänge zwischen0
und2^k - 1
( SHA1 gibt Werte zwischen0
und an2^160
). Bisher haben wir also erreicht, dass wir die Anzahl der eindeutigen Elemente mit der maximalen Kardinalität derk
Bits nur durch Speichern abschätzen können Eine Anzahl vonlog(k)
Größenbits. Der Nachteil ist, dass wir eine große Abweichung in unserer Schätzung haben. Eine coole Sache, die wir fast geschaffen habenDas probabilistische Zählpapier von 1984 (es ist ein bisschen schlauer mit der Schätzung, aber wir sind immer noch nah dran).LogLog
Bevor wir weitermachen, müssen wir verstehen, warum unsere erste Schätzung nicht so gut ist. Der Grund dafür ist, dass ein zufälliges Auftreten eines hochfrequenten 0-Präfix-Elements alles verderben kann. Eine Möglichkeit, dies zu verbessern, besteht darin, viele Hash-Funktionen zu verwenden, das Maximum für jede der Hash-Funktionen zu zählen und sie am Ende zu mitteln. Dies ist eine ausgezeichnete Idee, die die Schätzung verbessern wird, aber LogLog-Papier verwendete einen etwas anderen Ansatz (wahrscheinlich, weil Hashing ziemlich teuer ist).
Sie verwendeten einen Hash, teilten ihn aber in zwei Teile. Einer wird als Bucket bezeichnet (die Gesamtzahl der Buckets ist
2^x
) und ein anderer - entspricht im Grunde unserem Hash. Es war schwer für mich zu verstehen, was los war, also werde ich ein Beispiel geben. Angenommen , Sie haben zwei Elemente und Ihre Hash - Funktion , die Werte Form gibt0
zu2^10
2 erzeugten Werte:344
und387
. Sie haben sich für 16 Eimer entschieden. Also hast du:Wenn Sie mehr Eimer haben, verringern Sie die Varianz (Sie verbrauchen etwas mehr Platz, aber es ist immer noch winzig). Mit mathematischen Fähigkeiten konnten sie den Fehler quantifizieren (was ist
1.3/sqrt(number of buckets)
).HyperLogLog
HyperLogLog führt keine neuen Ideen ein, verwendet jedoch meistens viel Mathematik, um die vorherige Schätzung zu verbessern. Forscher haben herausgefunden, dass Sie die Schätzung erheblich verbessern, wenn Sie 30% der größten Zahlen aus den Eimern entfernen. Sie verwendeten auch einen anderen Algorithmus zur Mittelung von Zahlen. Das Papier ist mathematisch schwer.
Und ich möchte mit einem kürzlich erschienenen Artikel abschließen , der eine verbesserte Version des hyperLogLog-Algorithmus zeigt (bis jetzt hatte ich keine Zeit, ihn vollständig zu verstehen, aber vielleicht werde ich diese Antwort später verbessern).
quelle
k zeroes
nichts Besonderes ist. Sie können stattdessen suchenk ones
und die Logik wäre die gleiche oder sogar nach einerk length
Zeichenfolge suchen,{0,1}
aber nehmen Sie eine solche Zeichenfolge und bleiben Sie dabei? weil alle von ihnen bei solchen binären Strings die gleiche Wahrscheinlichkeit von 1/2 ^ k haben?Die Intuition ist, wenn Ihre Eingabe eine große Menge von Zufallszahlen ist (z. B. Hash-Werte), sollten sie sich gleichmäßig über einen Bereich verteilen. Angenommen, der Bereich beträgt bis zu 10 Bit, um einen Wert von bis zu 1024 darzustellen. Dann wird der Mindestwert beobachtet. Nehmen wir an, es ist 10. Dann wird die Kardinalität auf ungefähr 100 (10 × 100 ≈ 1024) geschätzt.
Lesen Sie das Papier für die wahre Logik natürlich.
Eine weitere gute Erklärung mit Beispielcode finden Sie hier:
Verdammt coole Algorithmen: Kardinalitätsschätzung - Nicks Blog
quelle