2016-03-23 5 views
1

Das Problem besteht darin, Frequenzen jedes Elements eines Arrays von Real zu finden.Schnellster Algorithmus zum Finden von Häufigkeiten für jedes Element eines Real-Arrays?

double[] a = new double[n] 
int[] freq = new int[n] 

Ich habe mit zwei Lösung zu kommen:

Erste Lösung O (n^2):

for (int i = 0; i < a.length; i++) { 
    if (freq[i] != -1) { 
    for (int j = i + 1; j < a.length; j++) { 
     if (a[i] == a[j]) { 
     freq[i]++; 
     freq[j] = -1; 
     } 
    } 
    } 
} 

Zweite Lösung O (n log n):

quickSort(a, 0, a.length - 1); 

freq[j] = 1; 
for (int i = 0; i < a.length - 1; i++) { 
    if (a[i] == a[i + 1]) { 
    freq[j]++; 
    } 
    else { 
    j = i + 1; 
    freq[j] = 1; 
    } 
} 

Ist Gibt es einen schnelleren Algorithmus für dieses Problem (O (n) vielleicht)? Vielen Dank im Voraus für jede Hilfe, die Sie zur Verfügung stellen können.

+5

Die Überprüfung der Identität von 'double's ist keine gute Praxis. [Was jeder Programmierer über Fließkomma-Punkte wissen sollte] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – amit

+2

Als eine Randnotiz ist dies Element Unterscheidbarkeit Problem, und es gibt keine O (n) Lösung unter dem algebraischen Baummodell. Wenn Sie jedoch mit der Identität der Doppelgänger bleiben, können Sie eine Hash-Tabelle verwenden, aber wieder - das ist eine schlechte Übung. – amit

+0

@amit warum ist es eine schlechte Praxis, Hash-Tabellen in Fällen wie oben zu verwenden? – sAm

Antwort

1

Ihr verdoppelt wurden entsprechend bereits gerundet und Sie sind zuversichtlich, es nicht ist Dies verwendet ziemlich viel Speicher ein Fehler befürchten Sie eine Hash-map wie

Map<Double, Long> freqCount = DoubleStream.of(reals).boxed() 
     .collect(Collectors.groupingBy(d -> d, Collectors.counting())); 

verwenden können, aber ist O (n).

Die Alternative ist die folgende als erste Pass

NavigableMap<Double, Long> freqCount = DoubleStream.of(reals).boxed() 
     .collect(Collectors.groupingBy(d -> d, TreeMap::new, Collectors.counting())); 

Diese alle Werte, die genau gleich sind, werden zählen zu verwenden, und Sie können eine Gruppierung Strategie verwenden doppelte Werte zu kombinieren, die fast gleich sind , sollte aber für Ihre Zwecke als gleich angesehen werden. Dies ist O (N log N)

10

Lassen Sie mich damit beginnen, dass die Überprüfung der Identität double s ist keine gute Praxis. Für weitere Details siehe: What every programmer should know about floating points.
Sie sollten robustere double Vergleiche verwenden.

Jetzt, dass wir damit fertig sind, stellen wir uns Ihrem Problem.
Sie haben es mit der Variante Element Distinctness Problem mit Gleitkommazahl zu tun.

Im Allgemeinen, unter dem algebraischen Baum Berechnungsmodell, kann man es nicht besser machen als Omega(nlogn) (Referenzen in diesem Thema: https://stackoverflow.com/a/7055544/572670).

Wenn Sie jedoch mit der double s Identitätskontrollen halten werden (bitte nicht), können Sie ein stärkeres Modell und Hash-Tabelle verwenden O(n) Lösung zu erreichen, indem eine Hash-Tabelle histogram Basis beibehalten (implementiert als HashMap<Double,Integer>) der Elemente, und wenn Sie fertig sind, scannen Sie das Histogramm und geben Sie den Schlüssel mit dem höchsten Wert.
(Bitte tun Sie es nicht)


Es ist eine komplexe Art und Weise O(n) Zeit basierend auf Hashing zu tun zu erreichen, auch wenn sie mit schwimmenden Punkte zu tun. Dies basiert auf dem Hinzufügen von Elementen zu mehreren Einträgen der Hash-Tabelle und unter der Annahme, dass eine Hash-Funktion einen Bereich von Elementen [x-delta/2,x+delta/2) auf den gleichen Hash-Wert annimmt (so ist es Hashing in Chunks [x1,x2)->h1, [x2,x3)->h2, [x3,x4)->h3, ....). Sie können dann eine Hash-Tabelle erstellen, in der ein Element x mit 3 Werten hashed wird: x-3/4delta, x, x + 3/4delta.
Dies garantiert, dass bei der Überprüfung eines gleichen Wertes später eine Übereinstimmung in mindestens einer der 3 Stellen, an denen Sie das Element platziert haben, vorhanden ist.

Dies ist wesentlich komplexer zu implementieren, aber es sollte funktionieren. Eine Variante davon kann in cracking the code interview, mathematische Frage 6. gefunden werden (So stellen Sie sicher, dass bei Ausgabe 5 aussehen, ist die Antwort in Ausgabe 4 falsch und wurde in der neueren Ausgabe festgelegt)


Als eine andere Seite Beachten Sie, dass Sie Ihre eigene Sortierung nicht implementieren müssen. Verwenden Sie

0

Die Verwendung eines Trie würde in ziemlich linearer Zeit durchführen, da Insertionen extrem schnell sein werden (oder so schnell wie die Reihenfolge Ihrer reellen Zahl).

Das Sortieren und Zählen ist definitiv viel zu langsam, wenn Sie nur die Frequenzen brauchen. Ihr Freund ist der Trie: https://en.wikipedia.org/wiki/Trie

Wenn Sie eine Trie verwenden, dann würden Sie jede ganze Zahl in einen String konvertieren (einfach genug in Java). Die Komplexität einer Einfügung in eine Trie variiert leicht basierend auf der Implementierung, aber im Allgemeinen wird sie proportional zur Länge der Zeichenfolge sein.

Wenn Sie eine Implementierung eines Trie müssen, schlage ich vor, ein Blick in Robert Sedgwick-Implementierung für den Kurs seines Algorithmus hier:

http://algs4.cs.princeton.edu/52trie/TrieST.java.html

+0

Das Erstellen des Binärbaums würde O (N log N) –

+0

True, herausgeschnitten. Ich denke, Trie ist so nah wie linear, wie Sie – libby

+0

bekommen werden, es sei denn, Sie verwenden eine Hash-Karte. –

Verwandte Themen