Ich hatte diese Frage gestern bei einem Algorithmus-Test und kann die Antwort nicht herausfinden. Es macht mich absolut verrückt, weil es ungefähr 40 Punkte wert war. Ich denke, dass der Großteil der Klasse es nicht richtig gelöst hat, weil ich in den letzten 24 Stunden keine Lösung gefunden habe.
Suchen Sie bei einer beliebigen binären Zeichenfolge mit der Länge n drei gleichmäßig verteilte Zeichenfolgen innerhalb der Zeichenfolge, falls vorhanden. Schreiben Sie einen Algorithmus, der dies in O (n * log (n)) Zeit löst.
Zeichenfolgen wie diese haben also drei, die "gleichmäßig verteilt" sind: 11100000, 0100100100
Bearbeiten: Es ist eine Zufallszahl, daher sollte es für jede Zahl funktionieren können. Die Beispiele, die ich gab, sollten die Eigenschaft "gleichmäßig verteilt" veranschaulichen. 1001011 ist also eine gültige Nummer. Mit 1, 4 und 7 sind diejenigen, die gleichmäßig verteilt sind.
Antworten:
Schließlich! Nach den Hinweisen in der Antwort von sdcvvc haben wir es: den O (n log n) -Algorithmus für das Problem! Es ist auch einfach, nachdem Sie es verstanden haben. Diejenigen, die FFT vermuteten, hatten Recht.
Das Problem: Wir erhalten eine binäre Zeichenfolge mit
S
der Länge n und möchten drei gleichmäßig verteilte Einsen darin finden. Zum BeispielS
kann sein110110010
, wobei n = 9 ist. Es hat einen gleichmäßigen Abstand von 1s an den Positionen 2, 5 und 8.Scannen Sie von
S
links nach rechts und erstellen Sie eine ListeL
mit Positionen von 1. Für dieS=110110010
obigen Angaben haben wir die Liste L = [1, 2, 4, 5, 8]. Dieser Schritt ist O (n). Das Problem ist nun eine finden arithmetische Progression der Länge 3 inL
, das heißt zu finden deutliche a, b, c inL
derart , daß ba = cb oder äquivalent a + c = 2b . Für das obige Beispiel wollen wir den Verlauf finden (2, 5, 8).Machen Sie ein Polynom
p
mit Termen x k für jedes k inL
. Für das obige Beispiel machen wir das Polynom p (x) = (x + x 2 + x 4 + x 5 + x 8 ) . Dieser Schritt ist O (n).Finden Sie das Polynom
q
= p 2 mit der Fast Fourier Transform . Für das obige Beispiel erhalten wir das Polynom q (x) = x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2 . Dieser Schritt ist O (n log n).Ignorieren Sie alle Begriffe außer denen, die x 2k für einige k in entsprechen
L
. Für das obige Beispiel erhalten wir die Terme x 16 , 3x 10 , x 8 , x 4 , x 2 . Dieser Schritt ist O (n), wenn Sie dies überhaupt tun möchten.Hier ist der entscheidende Punkt: der Koeffizient jeden x 2b für b in
L
ist genau die Anzahl von Paaren (a, c) inL
derart , daß a + c = 2b . [CLRS, Bsp. 30.1-7] Ein solches Paar ist immer (b, b) (der Koeffizient ist also mindestens 1), aber wenn es ein anderes Paar (a, c) gibt , dann ist der Koeffizient mindestens 3 von (a, c) ) und (c, a) . Für das obige Beispiel haben wir den Koeffizienten von x 10 genau wegen des AP (2,5,8) auf 3. (Diese Koeffizienten x 2bwird aus den oben genannten Gründen immer ungerade Zahlen sein. Und alle anderen Koeffizienten in q sind immer gerade.)Der Algorithmus besteht also darin, die Koeffizienten dieser Terme x 2b zu betrachten und festzustellen , ob einer von ihnen größer als 1 ist. Wenn es keine gibt, gibt es keine gleichmäßig verteilten 1s. Wenn es ist ein b in ,
L
für die der Koeffizient der x 2b größer als 1 ist, dann wissen wir , dass es einige Paare (a, c) - andere als (b, b) - für welches ein + c = 2b . Um das tatsächliche Paar zu finden, versuchen wir einfach jedes a inL
(das entsprechende c wäre 2b-a ) und prüfen, ob es an Position 2b-a in eine 1 gibtS
. Dieser Schritt ist O (n).Das war's Leute.
Man könnte fragen: Müssen wir FFT verwenden? Viele Antworten, wie Beta des , Fly-By-Wire ist , und rsp ist , deuten darauf hin , dass der Ansatz , dass die Kontrollen jedes Paar von 1s und sieht , wenn es eine 1 an der „dritte“ Position ist, kann Arbeit in O (n log n), auf der Grundlage der Intuition Wenn es zu viele Einsen gibt, finden wir leicht ein Tripel, und wenn es zu wenige Einsen gibt, dauert es wenig Zeit, alle Paare zu überprüfen. Leider, während diese Intuition richtig und der einfache Ansatz ist ist besser als O (n 2 ), ist es nicht wesentlich besser. Wie in der Antwort von sdcvvc können wir die "Cantor-ähnliche Menge" von Strings der Länge n = 3 k nehmenmit 1s an den Positionen, deren ternäre Darstellung nur 0s und 2s (keine 1s) enthält. Eine solche Zeichenfolge enthält 2 k = n (log 2) / (log 3) ≈ n 0,63 Einsen und keine gleichmäßig verteilten 1s. Die Überprüfung aller Paare würde also in der Größenordnung des Quadrats der Anzahl der 1s liegen: das ist 4 k ≈ n 1,26, was leider asymptotisch viel größer ist als (n log n). In der Tat ist der schlimmste Fall noch schlimmer: Leo Moser konstruierte 1953 (effektiv) solche Strings, die n 1-c / √ (log n) 1s enthalten, aber keine gleichmäßig verteilten 1s, was bedeutet, dass auf solchen Strings die einfachen Ansatz würde Θ (n 2-2c / √ (log n) ) nehmen- überraschenderweise nur ein kleines bisschen besser als Θ (n 2 ) !
Ungefähr die maximale Anzahl von 1s in einer Zeichenfolge mit der Länge n ohne 3 gleichmäßig verteilte (was wir oben gesehen haben, war mindestens n 0,63 aus der einfachen Cantor-ähnlichen Konstruktion und mindestens n 1-c / √ (log n) mit Mosers Konstruktion) - das ist OEIS A003002 . Sie kann auch direkt aus OEIS A065825 als k berechnet werden, so dass A065825 (k) ≤ n <A065825 (k + 1) ist. Ich habe ein Programm geschrieben, um diese zu finden, und es stellt sich heraus, dass der Greedy-Algorithmus nicht die längste solche Zeichenfolge liefert . Zum Beispiel können wir für n = 9 5 1s (110100011) erhalten, aber der Gierige gibt nur 4 (110110000) für n= 26 wir können 11 1s erhalten (11001010001000010110001101), aber der Gierige gibt nur 8 (1101100001101100000000000000), und für n = 74 können wir 22 1s erhalten (11000010110001000001011010001000000000000000010010100001000010000100 Sie stimmen jedoch an einigen Stellen bis 50 überein (z. B. alle 38 bis 50). Wie aus den OEIS-Referenzen hervorgeht, scheint Jaroslaw Wroblewski an dieser Frage interessiert zu sein, und er unterhält eine Website zu diesen nicht gemittelten Sets . Die genauen Zahlen sind nur bis 194 bekannt.
quelle
Ihr Problem wird in diesem Artikel (1999) als DURCHSCHNITTLICH bezeichnet :
Wikipedia :
Dies ist genug, um Ihr Problem zu lösen :).
Was sehr wichtig ist, ist, dass O (n log n) Komplexität in Bezug auf die Anzahl der Nullen und Einsen ist, nicht die Anzahl der Einsen (die als Array angegeben werden könnten, wie [1,5,9,15]). Es ist schwierig zu überprüfen, ob eine Menge eine arithmetische Folge hat, ausgedrückt als Anzahl von Einsen, und laut dieser Veröffentlichung ist ab 1999 kein schnellerer Algorithmus als O (n 2 ) bekannt, und es wird vermutet, dass er nicht existiert. Jeder, der dies nicht berücksichtigt, versucht, ein offenes Problem zu lösen.
Andere interessante Informationen, meistens irrelevant:
Untergrenze:
Eine einfache Untergrenze ist eine Cantor-ähnliche Menge (Zahlen 1..3 ^ n-1, die in ihrer ternären Expansion keine 1 enthält) - ihre Dichte beträgt n ^ (log_3 2) (ca. 0,631). Es reicht also nicht aus, zu überprüfen, ob die Menge nicht zu groß ist, und dann alle Paare zu überprüfen, um O (n log n) zu erhalten. Sie müssen die Sequenz intelligenter untersuchen. Eine bessere niedriger ist gebunden zitierte hier - es ist n 1-c / (log (n)) ^ (1/2) . Dies bedeutet, dass das Cantor-Set nicht optimal ist.
Obergrenze - mein alter Algorithmus:
Es ist bekannt, dass für großes n eine Teilmenge von {1,2, ..., n}, die keine arithmetische Folge enthält, höchstens n / (log n) ^ (1/20) Elemente enthält. Die Arbeit Über Tripel in arithmetischer Folge beweist mehr: Die Menge darf nicht mehr als n * 2 28 * (log log n / log n) 1/2 Elemente enthalten. Sie können also überprüfen, ob diese Grenze erreicht ist, und wenn nicht, naiv Paare überprüfen. Dies ist der O (n 2 * log log n / log n) -Algorithmus, schneller als O (n 2 ). Leider ist "On triples ..." auf Springer - aber die erste Seite ist verfügbar, und die Darstellung von Ben Green ist hier verfügbar , Seite 28, Satz 24.
Die Papiere stammen übrigens aus dem Jahr 1999 - im selben Jahr wie das erste, das ich erwähnt habe. Deshalb erwähnt das erste wahrscheinlich dieses Ergebnis nicht.
quelle
Dies ist keine Lösung, sondern eine ähnliche Denkrichtung wie Olexiy
Ich habe mit dem Erstellen von Sequenzen mit maximaler Anzahl von Einsen herumgespielt, und sie sind alle sehr interessant. Ich habe bis zu 125 Stellen erhalten. Hier sind die ersten drei Zahlen, die beim Versuch gefunden wurden, so viele '1'-Bits wie möglich einzufügen:
Beachten Sie, dass es sich bei allen um Fraktale handelt (angesichts der Einschränkungen nicht allzu überraschend). Es kann etwas sein, rückwärts zu denken. Wenn die Zeichenfolge kein Fraktal mit einem Merkmal ist, muss sie ein sich wiederholendes Muster haben.
Vielen Dank an Beta für den besseren Begriff, um diese Zahlen zu beschreiben.
Update: Leider sieht es so aus, als würde das Muster zusammenbrechen, wenn mit einer ausreichend großen Anfangszeichenfolge begonnen wird, z. B.: 10000000000001:
quelle
Ich vermute, dass ein einfacher Ansatz, der wie O (n ^ 2) aussieht, tatsächlich etwas Besseres ergibt, wie O (n ln (n)). Die Sequenzen, deren Test am längsten dauert (für jedes gegebene n), enthalten keine Trios und begrenzen die Anzahl der Einsen, die in der Sequenz enthalten sein können, stark.
Ich habe mir einige handwedelnde Argumente ausgedacht, aber ich konnte keinen ordentlichen Beweis finden. Ich werde einen Stich in die Dunkelheit machen: Die Antwort ist eine sehr kluge Idee, die der Professor so lange gekannt hat, dass es offensichtlich erscheint, aber es ist viel zu schwer für die Studenten. (Entweder das oder du hast die Vorlesung durchgeschlafen.)
quelle
Revision: 2009-10-17 23:00
Ich habe dies mit einer großen Anzahl (wie Zeichenfolgen von 20 Millionen) ausgeführt und glaube jetzt, dass dieser Algorithmus nicht O (n logn) ist. Trotzdem ist es eine ausreichend coole Implementierung und enthält eine Reihe von Optimierungen, die es sehr schnell laufen lassen. Es wertet alle Anordnungen von Binärzeichenfolgen mit 24 oder weniger Ziffern in weniger als 25 Sekunden aus.
Ich habe den Code aktualisiert, um die
0 <= L < M < U <= X-1
Beobachtung von heute früher aufzunehmen.Original
Dies ähnelt im Konzept einer anderen Frage, die ich beantwortet habe . Dieser Code untersuchte auch drei Werte in einer Reihe und stellte fest, ob ein Triplett eine Bedingung erfüllte. Hier ist der daraus angepasste C # -Code:
Die Hauptunterschiede sind:
Dieser Code generiert einen Datensatz, um die schwierigste Eingabe für diesen Algorithmus zu finden.
Der Code für die vorherige Frage hat alle Lösungen mit einem Python-Generator generiert. Dieser Code zeigt nur das Schwierigste für jede Musterlänge an.
Dieser Code überprüft den Abstand vom mittleren Element zum linken und rechten Rand. Der Python-Code testete, ob eine Summe über oder unter 0 lag.
Der aktuelle Code arbeitet von der Mitte zum Rand, um einen Kandidaten zu finden. Der Code im vorherigen Problem arbeitete von den Rändern zur Mitte. Diese letzte Änderung führt zu einer großen Leistungsverbesserung.
Basierend auf den Beobachtungen am Ende dieses Aufsatzes durchsucht der Code Paare von geraden Zahlen von Paaren von ungeraden Zahlen, um L und U zu finden, wobei M fest bleibt. Dies reduziert die Anzahl der Suchvorgänge durch Vorberechnung von Informationen. Dementsprechend verwendet der Code zwei Indirektionsebenen in der Hauptschleife von FindCandidate und erfordert zwei Aufrufe von FindCandidate für jedes mittlere Element: einmal für gerade Zahlen und einmal für ungerade.
Die allgemeine Idee besteht darin, an Indizes zu arbeiten, nicht an der Rohdarstellung der Daten. Durch die Berechnung eines Arrays, in dem die Einsen erscheinen, kann der Algorithmus zeitlich proportional zur Anzahl der Einsen in den Daten und nicht zeitlich proportional zur Länge der Daten ausgeführt werden. Dies ist eine Standardtransformation: Erstellen Sie eine Datenstruktur, die einen schnelleren Betrieb ermöglicht und gleichzeitig das Problem gleichwertig hält.
Die Ergebnisse sind veraltet: entfernt.
Bearbeiten: 2009-10-16 18:48
Bei den Daten von yx, die in den anderen Antworten als repräsentativ für die zu berechnenden harten Daten anerkannt sind, erhalte ich diese Ergebnisse ... Ich habe sie entfernt. Sie sind veraltet.
Ich möchte darauf hinweisen, dass diese Daten für meinen Algorithmus nicht am schwierigsten sind, daher denke ich, dass die Annahme, dass die Fraktale von yx am schwierigsten zu lösen sind, falsch ist. Ich gehe davon aus, dass der schlimmste Fall für einen bestimmten Algorithmus vom Algorithmus selbst abhängt und wahrscheinlich nicht über verschiedene Algorithmen hinweg konsistent ist.
Bearbeiten: 2009-10-17 13:30
Weitere Beobachtungen dazu.
Konvertieren Sie zunächst die Zeichenfolge von Nullen und Einsen in ein Array von Indizes für jede Position der Einsen. Angenommen, die Länge dieses Arrays A ist X. Dann ist das Ziel zu finden
so dass
oder
Da A [L] und A [U] eine gerade Zahl ergeben, können sie nicht (gerade, ungerade) oder (ungerade, gerade) sein. Die Suche nach einer Übereinstimmung könnte verbessert werden, indem A [] in ungerade und gerade Pools aufgeteilt wird und nach Übereinstimmungen auf A [M] in den Pools von ungeraden und geraden Kandidaten gesucht wird.
Dies ist jedoch eher eine Leistungsoptimierung als eine algorithmische Verbesserung, denke ich. Die Anzahl der Vergleiche sollte sinken, aber die Reihenfolge des Algorithmus sollte gleich sein.
Bearbeiten 2009-10-18 00:45
Eine weitere Optimierung fällt mir ein, genauso wie die Trennung der Kandidaten in gerade und ungerade. Da die drei Indizes zu einem Vielfachen von 3 addiert werden müssen (a, a + x, a + 2x - mod 3 ist 0, unabhängig von a und x), können Sie L, M und U in ihre mod 3-Werte trennen ::
Tatsächlich könnten Sie dies mit der geraden / ungeraden Beobachtung kombinieren und sie in ihre Mod 6-Werte aufteilen:
und so weiter. Dies würde eine weitere Leistungsoptimierung liefern, jedoch keine algorithmische Beschleunigung.
quelle
Konnte noch keine Lösung finden :(, habe aber einige Ideen.
Was ist, wenn wir von einem umgekehrten Problem ausgehen: Konstruieren Sie eine Sequenz mit der maximalen Anzahl von 1s und OHNE gleichmäßig verteilte Trios. Wenn Sie nachweisen können, dass die maximale Anzahl von Einsen o (n) ist, können Sie Ihre Schätzung verbessern, indem Sie nur die Liste der Einsen durchlaufen.
quelle
Das kann helfen ....
Dieses Problem reduziert sich auf Folgendes:
Zum Beispiel
[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ]
würden wir bei einer gegebenen Folge von eine Teilfolge von[ 3, 6, 5, 2, 2]
mit einem Präfix von[ 3, 6 ]
mit Präfixsumme von9
und einem Suffix von[ 5, 2, 2 ]
mit Suffixsumme von finden9
.Die Reduzierung ist wie folgt:
Zum Beispiel
[ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]
würden wir bei einer gegebenen Folge von die Reduktion von finden[ 1, 3, 4]
. Aus dieser Reduktion berechnen wir die zusammenhängende Teilfolge von[ 1, 3, 4]
, das Präfix von[ 1, 3]
mit Summe von4
und das Suffix von[ 4 ]
mit Summe von4
.Diese Reduzierung kann in berechnet werden
O(n)
.Leider bin ich mir nicht sicher, wohin ich von hier aus gehen soll.
quelle
Für den einfachen Problemtyp (dh Sie suchen drei "1" mit nur (dh null oder mehr) "0" dazwischen) ist es ganz einfach: Sie können die Sequenz einfach bei jeder "1" aufteilen und nach zwei benachbarten Teilsequenzen mit suchen die gleiche Länge (die zweite Teilsequenz ist natürlich nicht die letzte). Offensichtlich kann dies in O (n) Zeit erfolgen.
Für die komplexere Version (dh Sie suchen einen Index i und eine Lücke g > 0 so
s[i]==s[i+g]==s[i+2*g]=="1"
), bin ich mir nicht sicher, ob es eine O (n log n) -Lösung gibt, da möglicherweise O (n²) -Tripletts vorhanden sind diese Eigenschaft (denken Sie an eine Folge von allen, es gibt ungefähr n² / 2 solcher Drillinge). Natürlich suchen Sie nur eine davon, aber ich habe derzeit keine Ahnung, wie ich sie finden kann ...quelle
Eine lustige Frage, aber sobald Sie feststellen, dass das tatsächliche Muster zwischen zwei Einsen keine Rolle spielt, wird der Algorithmus zu:
In Code, JTest-Mode (Beachten Sie, dass dieser Code nicht so effizient geschrieben wurde, und ich habe einige Drucke hinzugefügt, um zu sehen, was passiert.)
quelle
Ich dachte an einen Divide-and-Conquer-Ansatz, der funktionieren könnte.
Zunächst müssen Sie bei der Vorverarbeitung alle Zahlen, die kleiner als die Hälfte Ihrer Eingabegröße ( n / 3) sind, in eine Liste einfügen .
Gegeben eine Zeichenfolge:
0000010101000100
(Beachten Sie, dass dieses spezielle Beispiel gültig ist)Fügen Sie alle Primzahlen (und 1) von 1 bis (16/2) in eine Liste ein: {1, 2, 3, 4, 5, 6, 7}
Dann teilen Sie es in zwei Hälften:
100000101 01000100
Machen Sie so weiter, bis Sie zu Zeichenfolgen der Größe 1 gelangen. Fügen Sie für alle Zeichenfolgen der Größe 1 mit einer 1 den Index der Zeichenfolge zur Liste der Möglichkeiten hinzu. Andernfalls geben Sie -1 für einen Fehler zurück.
Sie müssen auch eine Liste der noch möglichen Abstandsabstände zurückgeben, die jedem Startindex zugeordnet sind. (Beginnen Sie mit der Liste, die Sie oben erstellt haben, und entfernen Sie die Zahlen, während Sie fortfahren.) Hier bedeutet eine leere Liste, dass Sie nur mit einer 1 arbeiten und daher an dieser Stelle ein beliebiger Abstand möglich ist. Andernfalls enthält die Liste Abstände, die ausgeschlossen werden müssen.
Fahren Sie also mit dem obigen Beispiel fort:
1000 0101 0100 0100
10 00 01 01 01 00 01 00
1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0
Im ersten Kombinationsschritt haben wir jetzt acht Zweiergruppen. Im ersten Fall haben wir die Möglichkeit einer Menge, aber wir lernen, dass ein Abstand von 1 unmöglich ist, weil die andere Null da ist. Wir geben also 0 (für den Index) und {2,3,4,5,7} zurück, da ein Abstand von 1 unmöglich ist. Im zweiten haben wir nichts und geben so -1 zurück. Im dritten haben wir eine Übereinstimmung ohne Abstände in Index 5, also geben Sie 5, {1,2,3,4,5,7} zurück. Im vierten Paar geben wir 7 zurück, {1,2,3,4,5,7}. Im fünften geben Sie 9, {1,2,3,4,5,7} zurück. Im sechsten Fall geben Sie -1 zurück. Im siebten geben Sie 13 zurück, {1,2,3,4,5,7}. Im achten geben Sie -1 zurück.
Wenn wir noch einmal vier Vierergruppen kombinieren, haben wir:
1000
: Return (0, {4,5,6,7})0101
: Return (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7})0100
: Return (9, {3,4,5,6,7})0100
: Return (13, {3,4,5,6,7})Kombinieren zu Achtergruppen:
10000101
: Rückgabe (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7})01000100
: Rückgabe (9, {4,7}), (13, {3,4,5,6,7})Kombinieren zu einem Satz von 16:
10000101 01000100
Im weiteren Verlauf prüfen wir alle Möglichkeiten. Bis zu diesem Schritt haben wir Dinge hinterlassen, die über das Ende der Zeichenfolge hinausgingen, aber jetzt können wir alle Möglichkeiten prüfen.
Grundsätzlich überprüfen wir die erste 1 mit Abständen von 5 und 7 und stellen fest, dass sie nicht mit 1 übereinstimmen. (Beachten Sie, dass jede Prüfung KONSTANT und nicht linear ist.) Dann prüfen wir die zweite (Index 5) mit Abständen von 2, 3, 4, 5, 6 und 7 - oder wir würden, aber wir können seitdem bei 2 anhalten das passt tatsächlich zusammen.
Puh! Das ist ein ziemlich langer Algorithmus.
Ich weiß nicht 100%, ob es wegen des letzten Schritts O (n log n) ist , aber alles bis dahin ist definitiv O (n log n) , soweit ich das beurteilen kann. Ich werde später darauf zurückkommen und versuchen, den letzten Schritt zu verfeinern.
EDIT: Meine Antwort wurde geändert, um Welbogs Kommentar widerzuspiegeln. Entschuldigung für den Fehler. Ich werde später auch einen Pseudocode schreiben, wenn ich etwas mehr Zeit habe, um zu entziffern, was ich wieder geschrieben habe. ;-);
quelle
100010001
? Wenn ich Ihren Ansatz richtig verstehe, kann er nicht mit ihm übereinstimmen, da die richtige Antwort(0,{4})
nicht berechnet werden kann. Angesichts der Tatsache, dass Sie keine Primzahlen in Ihrer Liste benötigen, ist es einfach, pathologische Zeichenfolgen zu finden, die die Liste der Möglichkeiten, die Sie überprüfen müssen, auf einen höheren Wert als O (n log (n)) aufblähen, denke ich.Ich werde hier meine grobe Vermutung geben und diejenigen, die die Komplexität besser berechnen können, mir helfen, wie mein Algorithmus in O-Notation abschneidet
Ich habe keine Ahnung, wie ich die Komplexität dafür berechnen soll. Kann mir jemand helfen?
Bearbeiten: Fügen Sie Code hinzu, um meine Idee zu veranschaulichen
edit2: habe versucht meinen Code zu kompilieren und habe einige große Fehler gefunden, behoben
quelle
Ich habe mir so etwas ausgedacht:
Dies ist inspiriert von andycjw.
In Bezug auf die Komplexität könnte dies O (nlogn) sein, da wir bei jeder Rekursion durch zwei teilen.
Ich hoffe es hilft.
quelle
Ok, ich werde das Problem noch einmal untersuchen. Ich denke, ich kann einen O (n log (n)) - Algorithmus beweisen, der den bereits diskutierten ähnlich ist, indem ich einen ausgeglichenen Binärbaum verwende, um Abstände zwischen Einsen zu speichern. Dieser Ansatz wurde von der Beobachtung von Justice inspiriert, das Problem auf eine Liste von Entfernungen zwischen den Einsen zu reduzieren.
Könnten wir die Eingabezeichenfolge scannen, um einen ausgeglichenen Binärbaum um die Position von 1 zu erstellen, so dass jeder Knoten die Position der 1 speichert und jede Kante mit dem Abstand zur benachbarten 1 für jeden untergeordneten Knoten gekennzeichnet ist. Beispielsweise:
Dies kann in O (n log (n)) erfolgen, da für eine Zeichenfolge der Größe n jede Einfügung im schlimmsten Fall O (log (n)) benötigt.
Dann besteht das Problem darin, den Baum zu durchsuchen, um festzustellen, ob an einem Knoten ein Pfad von diesem Knoten durch das linke Kind vorhanden ist, der dieselbe Entfernung hat wie ein Pfad durch das rechte Kind. Dies kann für jeden Teilbaum rekursiv erfolgen. Beim Zusammenführen von zwei Teilbäumen in der Suche müssen die Abstände von Pfaden im linken Teilbaum mit den Abständen von Pfaden im rechten Teilbaum verglichen werden. Da die Anzahl der Pfade in einem Teilbaum proportional zu log (n) ist und die Anzahl der Knoten n ist, glaube ich, dass dies in O (n log (n)) Zeit erfolgen kann.
Habe ich etwas vergessen?
quelle
Dies schien ein lustiges Problem zu sein, also beschloss ich, es zu versuchen.
Ich gehe davon aus, dass 111000001 die ersten drei finden und erfolgreich sein würde. Im Wesentlichen ist die Anzahl der Nullen nach der 1 wichtig, da 0111000 gemäß Ihrer Definition mit 111000 identisch ist. Sobald Sie zwei Fälle von 1 gefunden haben, vervollständigt die nächste gefundene 1 die Trilogie.
Hier ist es in Python:
Dies ist ein erster Versuch, daher bin ich sicher, dass dies sauberer geschrieben werden könnte. Bitte listen Sie die Fälle auf, in denen diese Methode fehlschlägt.
quelle
Ich gehe davon aus, dass der Grund dafür, dass dies nlog (n) ist, auf Folgendes zurückzuführen ist:
Sie haben also n, log (n) und 1 ... O (nlogn)
Edit: Ups, mein schlechtes. Mein Gehirn hatte es so eingestellt, dass n / 2 logn war ... was es offensichtlich nicht ist (das Verdoppeln der Anzahl von Elementen verdoppelt immer noch die Anzahl von Iterationen in der inneren Schleife). Dies ist immer noch bei n ^ 2 und löst das Problem nicht. Na ja, zumindest muss ich Code schreiben :)
Implementierung in Tcl
quelle
Ich glaube, ich habe einen Weg gefunden, das Problem zu lösen, aber ich kann keinen formalen Beweis erstellen. Die Lösung, die ich gemacht habe, ist in Java geschrieben und verwendet einen Zähler 'n', um zu zählen, wie viele Listen- / Array-Zugriffe es macht. Daher sollte n kleiner oder gleich stringLength * log (stringLength) sein, wenn es korrekt ist. Ich habe es für die Zahlen 0 bis 2 ^ 22 versucht, und es funktioniert.
Zunächst wird die Eingabezeichenfolge durchlaufen und eine Liste aller Indizes erstellt, die eine Eins enthalten. Dies ist nur O (n).
Dann wählt es aus der Liste der Indizes einen ersten Index und einen zweiten Index aus, der größer als der erste ist. Diese beiden Indizes müssen diejenigen enthalten, da sie in der Liste der Indizes enthalten sind. Von dort kann der dritte Index berechnet werden. Wenn der inputString [dritterIndex] eine 1 ist, wird er angehalten.
}}
Zusätzlicher Hinweis: Der Zähler n wird nicht inkrementiert, wenn er über die Eingabezeichenfolge iteriert, um die Liste der Indizes zu erstellen. Diese Operation ist O (n), hat also ohnehin keinen Einfluss auf die Komplexität des Algorithmus.
quelle
O(n^2)
Algorithmus ist.Ein Einstieg in das Problem besteht darin, über Faktoren nachzudenken und sich zu verändern.
Beim Verschieben vergleichen Sie die Folge von Einsen und Nullen mit einer verschobenen Version von sich. Sie nehmen dann passende. Nehmen Sie dieses Beispiel um zwei verschoben:
Die resultierenden Einsen (bitweise UND-verknüpft) müssen alle Einsen darstellen, die gleichmäßig von zwei beabstandet sind. Das gleiche Beispiel um drei verschoben:
In diesem Fall gibt es keine Einsen, die gleichmäßig drei voneinander entfernt sind.
Was sagt dir das? Nun, dass Sie nur Schichten testen müssen, die Primzahlen sind. Angenommen, Sie haben zwei Einsen, die sechs voneinander entfernt sind. Sie müssten nur 'zwei' Schichten und 'drei' Schichten testen (da diese sechs teilen). Beispielsweise:
Die einzigen Verschiebungen, die Sie jemals überprüfen müssen, sind 2,3,5,7,11,13 usw. Bis zur Primzahl, die der Quadratwurzel der Größe der Ziffernfolge am nächsten liegt.
Fast gelöst?
Ich denke, ich bin einer Lösung näher. Grundsätzlich:
Ich denke, der größte Hinweis auf die Antwort ist, dass die schnellsten Sortieralgorithmen O (n * log (n)) sind.
FALSCH
Schritt 1 ist falsch, wie ein Kollege betont hat. Wenn wir Einsen an Position 2,12 und 102 haben, dann würden sie bei einem Modul von 10 alle die gleichen Reste haben und sind dennoch nicht gleich weit voneinander entfernt! Es tut uns leid.
quelle
Hier sind einige Gedanken, die sich trotz meiner Bemühungen nicht in einen Bogen zu wickeln scheinen. Dennoch könnten sie ein nützlicher Ausgangspunkt für die Analyse einer Person sein.
Betrachten Sie die vorgeschlagene Lösung wie folgt. Dies ist der Ansatz, den mehrere Leute vorgeschlagen haben, einschließlich meiner selbst in einer früheren Version dieser Antwort.
:)
Betrachten Sie nun Eingabezeichenfolgen wie die folgenden, für die es keine Lösung gibt:
Im Allgemeinen ist dies die Verkettung von k Zeichenketten der Form j 0, gefolgt von einer 1 für j von Null bis k-1.
Es ist zu beachten, dass die Längen der Teilzeichenfolgen 1, 2, 3 usw. sind. Die Problemgröße n hat also Teilzeichenfolgen der Längen 1 bis k, so dass n = k (k + 1) / 2 ist.
Beachten Sie, dass k auch die Anzahl der Einsen verfolgt, die wir berücksichtigen müssen. Denken Sie daran, dass wir jedes Mal, wenn wir eine 1 sehen, alle bisher gesehenen 1 berücksichtigen müssen. Wenn wir also die zweite 1 sehen, betrachten wir nur die erste, wenn wir die dritte 1 sehen, überdenken wir die ersten beiden, wenn wir die vierte 1 sehen, müssen wir die ersten drei überdenken und so weiter. Am Ende des Algorithmus haben wir k (k-1) / 2 Paare von Einsen betrachtet. Nennen Sie das p.
Die Beziehung zwischen n und p ist, dass n = p + k ist.
Das Durchlaufen der Zeichenfolge dauert 0 (n) Zeit. Jedes Mal, wenn eine 1 angetroffen wird, werden maximal (k-1) Vergleiche durchgeführt. Da n = k (k + 1) / 2 ist, ist n> k ** 2, also sqrt (n)> k. Dies ergibt O (n sqrt (n)) oder O (n ** 3/2). Beachten Sie jedoch, dass dies möglicherweise keine wirklich enge Grenze ist, da die Anzahl der Vergleiche von 1 bis maximal k reicht und es nicht die ganze Zeit k ist. Aber ich bin mir nicht sicher, wie ich das in der Mathematik erklären soll.
Es ist immer noch nicht O (n log (n)). Ich kann auch nicht beweisen, dass diese Eingaben die schlimmsten Fälle sind, obwohl ich vermute, dass dies der Fall ist. Ich denke, eine dichtere Packung von 1 nach vorne führt zu einer noch spärlicheren Packung am Ende.
Da es vielleicht noch jemand nützlich findet, ist hier mein Code für diese Lösung in Perl:
quelle
Fügen Sie beim Scannen von 1s ihre Positionen einer Liste hinzu. Vergleichen Sie die zweiten und aufeinanderfolgenden Einsen mit jeder Position in der Liste, die Sie bisher erstellt haben. Der Abstand entspricht currentOne (Mitte) - previousOne (links). Das Bit auf der rechten Seite ist currentOne + Abstand. Wenn es 1 ist, das Ende.
Die Liste der Einsen wächst umgekehrt mit dem Abstand zwischen ihnen. Einfach ausgedrückt, wenn Sie zwischen den Einsen viele Nullen haben (wie im schlimmsten Fall), wächst Ihre Liste der bekannten Einsen ziemlich langsam.
quelle
Ich dachte, ich würde einen Kommentar hinzufügen, bevor ich die 22. naive Lösung für das Problem veröffentliche. Für die naive Lösung müssen wir nicht zeigen, dass die Anzahl der Einsen in der Zeichenfolge höchstens O (log (n)) beträgt, sondern höchstens O (sqrt (n * log (n)).
Löser:
Es ist im Grunde ein bisschen ähnlich wie die Idee und Implementierung von flybywire, obwohl es nach vorne statt nach hinten schaut.
Gieriger String Builder:
(Zu meiner Verteidigung bin ich immer noch in der Phase des "Learn Python" -Verständnisses)
Als potenziell nützliche Ausgabe des gierigen Aufbaus von Saiten gibt es einen ziemlich konstanten Sprung, nachdem eine Potenz von 2 in der Anzahl der Einsen erreicht wurde ... was ich nicht warten wollte, um Zeuge des Treffens von 2096 zu werden.
quelle
Ich werde versuchen, einen mathematischen Ansatz vorzustellen. Dies ist eher ein Anfang als ein Ende, daher wird jede Hilfe, jeder Kommentar oder sogar jeder Widerspruch zutiefst geschätzt. Wenn sich dieser Ansatz jedoch bewährt hat, ist der Algorithmus eine einfache Suche in der Zeichenfolge.
Bei einer festgelegten Anzahl von Leerzeichen
k
und einer ZeichenfolgeS
dauert die Suche nach einem Triplett mit k AbständenO(n)
- Wir testen einfach für jedes0<=i<=(n-2k)
WennS[i]==S[i+k]==S[i+2k]
. Der Test dauertO(1)
und wir machen esn-k
mal wok
eine Konstante ist, also dauert esO(n-k)=O(n)
.Nehmen wir an, dass es einen umgekehrten Anteil zwischen der Anzahl der
1
und den maximalen Leerzeichen gibt, nach denen wir suchen müssen. Das heißt, wenn es viele1
gibt, muss es ein Triplett geben und es muss ziemlich dicht sein; Wenn es nur wenige gibt1
, kann das Triplett (falls vorhanden) ziemlich spärlich sein. Mit anderen Worten, ich kann beweisen, dass ein1
solches Triplett existieren muss , wenn ich genug habe - und je mehr1
ich habe, desto dichter muss das Triplett gefunden werden. Dies kann durch das Pigeonhole-Prinzip erklärt werden - ich hoffe, darauf später näher eingehen zu können.Angenommen, Sie haben eine Obergrenze
k
für die mögliche Anzahl von Leerzeichen, nach denen ich suchen muss. Nun, für jede1
Lage inS[i]
wir überprüfen müssen1
inS[i-1]
undS[i+1]
,S[i-2]
undS[i+2]
, ...S[i-k]
undS[i+k]
. Dies geschiehtO((k^2-k)/2)=O(k^2)
für jede1
inS
- aufgrund Gauss' Series Summenformel . Beachten Sie, dass dies von Abschnitt 1 abweicht - ich habek
als Obergrenze für die Anzahl der Leerzeichen, nicht als konstantes Leerzeichen.Wir müssen beweisen
O(n*log(n))
. Das heißt, wir müssen zeigen, dass diesk*(number of 1's)
proportional zu istlog(n)
.Wenn wir das schaffen, ist der Algorithmus trivial - für jeden,
1
inS
dessen Index sich befindeti
, suchen Sie einfach nach1
's von jeder Seite bis zur Entfernungk
. Wenn zwei in der gleichen Entfernung gefunden wurden, kehren Sie zurücki
undk
. Wieder wäre der schwierige Teil zu findenk
die Richtigkeit und zu beweisen.Ich würde mich sehr über Ihre Kommentare hier freuen - ich habe bisher erfolglos versucht, die Beziehung zwischen
k
und die Anzahl der1
auf meinem Whiteboard zu finden .quelle
Annahme:
Es ist einfach falsch, über die log (n) Anzahl der Obergrenzen von Einsen zu sprechen
BEARBEITEN:
Jetzt fand ich heraus, dass bei Verwendung von Cantor-Zahlen (falls korrekt) die Dichte am Set (2/3) ^ Log_3 (n) ist (was für eine seltsame Funktion) und ich stimme zu, dass die Dichte von log (n) / n zu stark ist.
Wenn dies die Obergrenze ist, gibt es einen Algorithmus, der dieses Problem in mindestens O (n * (3/2) ^ (log (n) / log (3))) Zeitkomplexität und O ((3/2) ^ ( log (n) / log (3))) Raumkomplexität. (Überprüfen Sie die Antwort der Justiz auf Algorithmus)
Dies ist immer noch weitaus besser als O (n ^ 2)
Diese Funktion ((3/2) ^ (log (n) / log (3))) sieht auf den ersten Blick wirklich wie n * log (n) aus.
Wie habe ich diese Formel bekommen?
Cantors Nummer auf Saite spielen.
Angenommen, die Länge der Zeichenfolge beträgt 3 ^ p == n
Bei jedem Schritt bei der Erzeugung der Cantor-Zeichenfolge behalten Sie 2/3 der vorherigen Anzahl von Einsen. Wenden Sie diese p-mal an.
Das bedeutet (n * ((2/3) ^ p)) -> (((3 ^ p)) * ((2/3) ^ p)) verbleibende und nach Vereinfachung 2 ^ p. Dies bedeutet 2 ^ p Einsen in 3 ^ p Strings -> (3/2) ^ p Einsen. Ersetze p = log (n) / log (3) und erhalte
((3/2) ^ (log (n) / log (3)))
quelle
Wie wäre es mit einer einfachen O (n) -Lösung mit O (n ^ 2) -Raum? (Verwendet die Annahme, dass alle bitweisen Operatoren in O (1) arbeiten.)
Der Algorithmus arbeitet grundsätzlich in vier Schritten:
Stufe 1: Finden Sie für jedes Bit in Ihrer ursprünglichen Nummer heraus, wie weit diese entfernt sind, berücksichtigen Sie jedoch nur eine Richtung. (Ich habe alle Bits in Richtung des niedrigstwertigen Bits betrachtet.)
Stufe 2: Kehren Sie die Reihenfolge der Bits in der Eingabe um.
Stufe 3: Führen Sie Schritt 1 am umgekehrten Eingang erneut aus.
Stufe 4: Vergleichen Sie die Ergebnisse von Stufe 1 und Stufe 3. Wenn Bits über UND unter gleichem Abstand liegen, müssen wir einen Treffer erzielen.
Beachten Sie, dass kein Schritt im obigen Algorithmus länger dauert als O (n). ^ _ ^
Als zusätzlichen Vorteil findet dieser Algorithmus ALLE gleich beabstandeten von JEDER Zahl. Wenn Sie beispielsweise das Ergebnis "0x0005" erhalten, befinden sich BEIDE Einheiten 1 und 3 in gleichem Abstand
Ich habe nicht wirklich versucht, den folgenden Code zu optimieren, aber es ist kompilierbarer C # -Code, der zu funktionieren scheint.
Jemand wird wahrscheinlich kommentieren, dass für eine ausreichend große Anzahl bitweise Operationen in O (1) nicht ausgeführt werden können. Du hättest recht. Ich würde jedoch vermuten, dass jede Lösung, die Addition, Subtraktion, Multiplikation oder Division verwendet (was nicht durch Verschieben möglich ist), auch dieses Problem haben würde.
quelle
Unten ist eine Lösung. Hier und da könnte es einige kleine Fehler geben, aber die Idee ist richtig.
Bearbeiten: Es ist nicht n * log (n)
PSEUDO-CODE:
C # -Code:
Wie es funktioniert:
quelle
Offensichtlich müssen wir mindestens eine Reihe von Drillingen gleichzeitig überprüfen, also müssen wir die Überprüfungen irgendwie komprimieren. Ich habe einen Kandidatenalgorithmus, aber die Analyse der Zeitkomplexität liegt außerhalb meiner Fähigkeit * Zeitschwelle.
Erstellen Sie einen Baum, in dem jeder Knoten drei untergeordnete Knoten hat und jeder Knoten die Gesamtzahl der Einsen an seinen Blättern enthält. Erstellen Sie auch eine verknüpfte Liste über den Einsen. Weisen Sie jedem Knoten zulässige Kosten zu, die proportional zu dem Bereich sind, den er abdeckt. Solange die Zeit, die wir an jedem Knoten verbringen, innerhalb des Budgets liegt, haben wir einen O (n lg n) -Algorithmus.
- -
Beginnen Sie an der Wurzel. Wenn das Quadrat der Gesamtzahl der Einsen darunter unter den zulässigen Kosten liegt, wenden Sie den naiven Algorithmus an. Ansonsten auf seine Kinder zurückgreifen.
Jetzt sind wir entweder innerhalb des Budgets zurückgekehrt oder wir wissen, dass in einem der Kinder keine gültigen Drillinge vollständig enthalten sind. Daher müssen wir die Tripletts zwischen den Knoten überprüfen.
Jetzt wird es unglaublich chaotisch. Wir möchten im Wesentlichen auf die potenziellen Gruppen von Kindern zurückgreifen und gleichzeitig die Reichweite einschränken. Sobald der Bereich so eingeschränkt ist, dass der naive Algorithmus unter dem Budget ausgeführt wird, tun Sie dies. Viel Spaß beim Implementieren, denn ich garantiere, dass es langweilig wird. Es gibt wie ein Dutzend Fälle.
- -
Der Grund, warum ich denke, dass der Algorithmus funktionieren wird, ist, dass die Sequenzen ohne gültige Tripletts zwischen Bündeln von Einsen und vielen Nullen zu wechseln scheinen. Der nahe gelegene Suchraum wird effektiv aufgeteilt, und der Baum emuliert diese Aufteilung.
Die Laufzeit des Algorithmus ist überhaupt nicht offensichtlich. Es beruht auf den nicht trivialen Eigenschaften der Sequenz. Wenn die Einsen wirklich spärlich sind, funktioniert der naive Algorithmus unter Budget. Wenn die Einsen dicht sind, sollte sofort eine Übereinstimmung gefunden werden. Aber wenn die Dichte 'genau richtig' ist (z. B. in der Nähe von ~ n ^ 0,63, was Sie erreichen können, indem Sie alle Bits an Positionen ohne '2'-Ziffer in Basis 3 setzen), weiß ich nicht, ob es funktionieren wird. Sie müssten beweisen, dass der Aufteilungseffekt stark genug ist.
quelle
Keine theoretische Antwort hier, aber ich habe ein schnelles Java-Programm geschrieben, um das Laufzeitverhalten als Funktion von k und n zu untersuchen, wobei n die Gesamtbitlänge und k die Anzahl der Einsen ist. Ich bin mit einigen der Antwortenden zusammen, die sagen, dass der "reguläre" Algorithmus, der alle Paare von Bitpositionen überprüft und nach dem 3. Bit sucht, obwohl es im schlimmsten Fall O (k ^ 2) erfordern würde, in Realität, weil der schlimmste Fall spärliche Bitstrings benötigt, ist O (n ln n).
Wie auch immer, hier ist das Programm unten. Es ist ein Programm im Monte-Carlo-Stil, das eine große Anzahl von NTRIALS-Versuchen für die Konstante n ausführt und zufällig Bitsets für einen Bereich von k-Werten unter Verwendung von Bernoulli-Prozessen mit einer Einsen-Dichte generiert, die zwischen spezifizierbaren Grenzen begrenzt ist, und die Laufzeit aufzeichnet Um ein Triplett mit gleichmäßig verteilten zu finden oder nicht zu finden, wird die Zeit in Schritten NICHT in der CPU-Zeit gemessen. Ich habe es für n = 64, 256, 1024, 4096, 16384 * ausgeführt (läuft noch), zuerst einen Testlauf mit 500000 Versuchen, um festzustellen, welche k-Werte die längste Laufzeit benötigen, dann einen weiteren Test mit 5000000 Versuchen mit verengten Versuchen. Dichtefokus, um zu sehen, wie diese Werte aussehen. Die längsten Laufzeiten treten bei sehr geringer Dichte auf (z. B. für n = 4096 liegen die Laufzeitspitzen im Bereich von k = 16-64, mit einer sanften Spitze für die mittlere Laufzeit bei 4212 Schritten @ k = 31, Die maximale Laufzeit erreichte einen Spitzenwert von 5101 Schritten (k = 58). Es sieht so aus, als würde es extrem große Werte von N erfordern, bis der O (k ^ 2) -Schritt im ungünstigsten Fall größer wird als der O (n) -Schritt, bei dem Sie den Bitstring scannen, um die Positionsindizes der 1 zu finden.
quelle
Ich habe Probleme mit den Worst-Case-Szenarien mit Millionen von Ziffern. Das Fuzzing von
/dev/urandom
ergibt im Wesentlichen O (n), aber ich weiß, dass der schlimmste Fall schlimmer ist. Ich kann nur nicht sagen, wie viel schlimmer. Für kleine Unternehmenn
ist es trivial, Inputs in der Nähe zu finden3*n*log(n)
, aber es ist überraschend schwierig, diese von einer anderen Wachstumsordnung für dieses spezielle Problem zu unterscheiden.Kann jemand, der an Worst-Case-Eingaben gearbeitet hat, eine Zeichenfolge mit einer Länge von mehr als beispielsweise einhunderttausend generieren?
quelle
Eine Anpassung des Rabin-Karp-Algorithmus könnte für Sie möglich sein. Seine Komplexität ist 0 (n), also könnte es Ihnen helfen.
Schauen Sie sich http://en.wikipedia.org/wiki/Rabin-Karp_string_search_algorithm an
quelle
Könnte dies eine Lösung sein? Ich bin mir nicht sicher, ob es O (nlogn) ist, aber meiner Meinung nach ist es besser als O (n²), weil der einzige Weg, kein Tripel zu finden, eine Primzahlverteilung wäre.
Es gibt Raum für Verbesserungen, die zweite gefundene 1 könnte die nächste erste sein 1. Auch keine Fehlerprüfung.
quelle
Ich denke, dieser Algorithmus hat O (n log n) Komplexität (C ++, DevStudio 2k5). Jetzt weiß ich nicht genau, wie ein Algorithmus analysiert werden muss, um seine Komplexität zu bestimmen. Daher habe ich dem Code einige Informationen zum Sammeln von Metriken hinzugefügt. Der Code zählt die Anzahl der Tests, die an der Folge von Einsen und Nullen für eine bestimmte Eingabe durchgeführt wurden (hoffentlich habe ich keine Bälle aus dem Algorithmus gemacht). Wir können die tatsächliche Anzahl der Tests mit dem O-Wert vergleichen und feststellen, ob eine Korrelation besteht.
Dieses Programm gibt die Anzahl der Tests für jede Zeichenfolgenlänge mit bis zu 32 Zeichen aus. Hier sind die Ergebnisse:
Ich habe auch die 'n log n' Werte hinzugefügt. Zeichnen Sie diese mit dem Grafikwerkzeug Ihrer Wahl, um eine Korrelation zwischen den beiden Ergebnissen festzustellen. Erstreckt sich diese Analyse auf alle Werte von n? Ich weiß es nicht.
quelle