Gibt es einen Algorithmus, der in


21

Ich möchte die Existenz eines Algorithmus beweisen oder widerlegen, der bei einem Array von ganzen Zahlen drei Indizes und so dass und (oder stellt fest, dass es kein solches Tripel gibt) in linearer Zeit.i , j k i < j < k A [ i ] < A [ j ] < A [ k ]Ai,jki<j<kA[i]<A[j]<A[k]

Dies ist keine Hausaufgabe. Ich habe es in einem Programmierforum gesehen, in dem es heißt: "Versuche einen solchen Algorithmus zu implementieren." Ich vermute, dass es nach verschiedenen Experimenten unmöglich ist. Meine Intuition sagt es mir, aber das zählt nicht wirklich für irgendetwas.

Ich würde es gerne formal beweisen. Wie machst du das? Ich würde im Idealfall gerne einen Beweis sehen, der Schritt für Schritt erstellt wird, und wenn Sie dazu geneigt sind, eine Erklärung, wie einfache Fragen wie diese im Allgemeinen zu beweisen / zu widerlegen sind. Wenn es hilft, einige Beispiele:

[1,5,2,0,3] → (1,2,3)
[5,6,1,2,3] → (1,2,3)
[1,5,2,3] → (1,2,3)
[5,6,1,2,7] → (1,2,7)
[5,6,1,2,7,8] → (1,2,7)
[1,2,999,3] → (1,2,999)
[999,1,2,3] → (1,2,3)
[11,12,8,9,5,6,3,4,1,2,3] → (1,2,3)
[1,5,2,0,-5,-2,-1] → (-5,-2,-1)

Ich nahm an, dass man über iterieren könnte , und jedes Mal, wenn es ein (unser aktuelles , das heißt) gibt, machen wir ein neues Tripel und schieben es auf ein Array. Wir gehen weiter und vergleichen jedes Tripel, bis eines unserer Tripel vollständig ist. So ist es wie , ! Ich halte dies jedoch für komplexer als nur da die Anzahl der Tripel in unserem Triple-Array im schlimmsten Fall der Größe der Eingabeliste entsprechen würde.i < j j O ( n )Ai<jj[1,5,2,0,-5,-2,-1] → 1..2.. -5.. -2.. -1[1,5,2,0,-5,-2,3,-1] → 1..2.. -5.. -2.. 3O(n)



Beachten Sie, dass Sie im schlimmsten Fall (sortiertes Array) sogar viele geeignete Tripel haben. Bitte geben Sie den von Ihnen vorgeschlagenen Algorithmus als Pseudocode an. Ich denke, deine Erklärung ist nicht vollständig. Θ(n3)
Raphael

Antworten:


14

Dies ist eine Variation des Problems mit der längsten zunehmenden Folge . Dies ist die auf Wikipedia vorgestellte Lösung, die zwei Hilfsarrays und P verwendet :MP

  • - speichert die Position k des kleinsten Wertes A [ k ], so dass es eine ansteigende Folge der Länge j gibt, die bei A [ k ] im Bereich k i endet(man beachte, dass wir hier j k i haben, weil j die Länge der ansteigenden Teilsequenz und k die Position ihrer Beendigung darstellt. Offensichtlich können wir niemals eine ansteigende Teilsequenz der Länge 13 haben, die an Position 11 endetM[j]kA[k]jA[k]kijkijk1311. per Definition).ki
  • - speichert die Position des Vorgängers von A [ k ] in der längsten aufsteigenden Folge, die bei A [ k ] endet.P[k]A[k]A[k]

    Zusätzlich speichert der Algorithmus eine Variable die die Länge der längsten aufsteigenden Teilsequenz darstellt, die bisher gefunden wurde.L

Dieser Algorithmus läuft im ungünstigsten Fall . Ihr Problem ist ein Sonderfall, der es Ihnen ermöglicht, zurückzukehren, wenn L = 3 ist, was die Laufzeit auf O ( n ) drückt, da die binäre Suche nur für Arrays mit einer Länge von höchstens zwei ausgeführt wird, was sich daher in der Zeit O ( 1 ) im Gegensatz zu befindet Θ ( log n ) im allgemeinen Fall.Θ(nlogn)L=3O(n)O(1)Θ(logn)

Betrachten Sie den modifizierten Pseudocode:

 L = 0
 for i = 1, 2, ... n:
    binary search for the largest positive j ≤ L
      such that X[M[j]] < X[i] (or set j = 0 if no such value exists)
    P[i] = M[j]
    if j == L or X[i] < X[M[j+1]]:
       M[j+1] = i
       L = max(L, j+1)
   if L==3 : return true; // you can break here, and return true.
return false; // because L is smaller than 3.

@ SaeedAmiri Ich habe den Kommentar gesehen, hatte aber noch keine Zeit, ihn zu lesen (ich habe die Frage gepostet, bevor ich ins Bett gegangen bin). Ich vermutete aus Ihrem Link, dass unser Sonderfall L = 3 irgendwie helfen würde, hatte aber keine Chance, die Details zu verstehen. Ich bin derzeit auf der Arbeit und zeitlich begrenzt. Seien Sie versichert, dass ich Ihre Antwort schätze. Es wäre oberflächlich von mir, Ihnen dafür zu danken, ohne jede Zeile darin vollständig zu verstehen.
Christopher Done

@ SaeedAmiri: Ich stimme dir zu, dass du hier mehr "Lücken füllen" erwartest, aber du musst immer noch die Eckargumente eines Beweises (wie skizzenhaft auch immer) angeben. In Bezug auf das OP scheint er in Italien stationiert zu sein, also hat er wahrscheinlich zwischen Ihrem Kommentar und Ihrer Antwort tief und fest geschlafen (und die Chancen stehen gut, dass er gerade mit Eastern beschäftigt ist).
Raphael

@ChristopherDone, ich möchte dich nicht verärgern, sorry, es ist mein Fehler, du hast definitiv recht.

+1: Dies verallgemeinert schön, macht nur einen Durchgang und ist Raum. O(1)
Aryabhata

OK, es sieht gut aus. Ich habe eine Weile gebraucht, um das Verhalten des allgemein am längsten wachsenden Sequenzalgorithmus zu untersuchen. Danach ist die Änderung der maximalen Länge == 3 in Ordnung. Vielen Dank!
Christopher Done

11

Ein Hinweis zur Methodik

Ich habe ein wenig über dieses Problem nachgedacht und bin zu einer Lösung gekommen. Als ich die Antwort von Saeed Amiri las , stellte ich fest, dass ich eine spezielle Version des Standardalgorithmus für die Suche nach der längsten Teilsequenz für eine Sequenz der Länge 3 gefunden hatte ist ein interessantes Beispiel für die Problemlösung.

Die Zwei-Elemente-Version

i<jA[i]<A[j]

Ai<j,A[i]A[j]i,A[i]A[i+1]iA[i]<A[i+1]

Dieser Fall ist sehr einfach; Wir werden versuchen, es zu verallgemeinern. Es zeigt sich, dass das angegebene Problem nicht lösbar ist: Die angeforderten Indizes existieren nicht immer. Wir werden daher eher darum bitten, dass der Algorithmus entweder gültige Indizes zurückgibt, wenn sie existieren, oder korrekt behauptet, dass keine solchen Indizes existieren.

Den Algorithmus finden

A(A[i1],,A[im])i1<<imA(A[i],A[i+1],,A[i+m1])

Wir haben gerade gesehen, dass die angeforderten Indizes nicht immer existieren. Unsere Strategie besteht darin, zu untersuchen, wann die Indizes nicht existieren. Wir werden dies tun, indem wir annehmen, dass wir versuchen, die Indizes zu finden und zu sehen, wie unsere Suche schief gehen könnte. Dann liefern die Fälle, in denen die Suche nicht schief geht, einen Algorithmus, um die Indizes zu finden.

4,3,2,1,0

j=i+1k=j+1A[i]<A[i+1]<A[i+2]

4,3,2,1,2,3,2,1,0

A[j]<A[j+1]iA[i]<A[j]kA[j+1]<A[k]

4,3,2,2,5,1,5,0,5,1,0

ik

3,2,1,3,5,2,5,1,5,0,5, -0,5,1,25, -0,25 3,2,1,2,5,1,5,0,5,2,1,0

ijki

2.1,3,2,1,2.5,1.5,0.5,2,1,0 1,2,0,2.5,1.5,0.5

i(i,j)ki(i,j)(i,j)i>jA[i]<A[i]ii(i,j)jA[j]<A[j](i,j)

Aussage des Algorithmus

In der Python-Syntax angegeben, aber beachten Sie, dass ich es nicht getestet habe.

def subsequence3(A):
    """Return the indices of a subsequence of length 3, or None if there is none."""
    index1 = None; value1 = None
    index2 = None; value2 = None
    for i in range(0,len(A)):
        if index1 == None or A[i] < value1:
            index1 = i; value1 = A[i]
        else if A[i] == value1: pass
        else if index2 == None:
            index2 = (index1, i); value2 = (value1, A[i])
        else if A[i] < value2[1]:
            index2[1] = i; value2[1] = A[i]
        else if A[i] > value2[1]:
            return (index2[0], index2[1], i)
    return None

Proof-Skizze

index1ist der Index des Minimums des Teils des Arrays, der bereits durchlaufen wurde (wenn er mehrmals auftritt, wird das erste Vorkommen beibehalten) oder Nonebevor das erste Element verarbeitet wird. index2speichert die Indizes der ansteigenden Teilsequenz der Länge 2 im bereits durchquerten Teil des Arrays, der das niedrigste größte Element hat, oder Nonewenn eine solche Sequenz nicht existiert.

Wenn return (index2[0], index2[1], i)läuft, haben wir value2[0] < value[1](dies ist eine Invariante von value2) und value[1] < A[i](aus dem Kontext ersichtlich). Wenn die Schleife endet, ohne die frühe Rückkehr aufzurufen value1 == None, gibt es in diesem Fall entweder keine zunehmende Teilfolge der Länge 2, geschweige denn der Länge 3, oder sie value1enthält die zunehmende Teilfolge der Länge 2, die das niedrigste größte Element aufweist. Im letzteren Fall haben wir weiterhin die Invariante, dass keine zunehmende Folge der Länge 3 früher endet als value1; daher würde das letzte Element einer solchen hinzugefügten value2Teilsequenz eine zunehmende Teilsequenz der Länge 3 bilden: da wir dort auch die Invariante haben, value2die nicht Teil einer zunehmenden Teilsequenz der Länge 3 ist, die in dem bereits durchquerten Teil des Arrays enthalten ist ist keine solche Untersequenz im gesamten Array.

Das Beweisen der vorgenannten Invarianten bleibt dem Leser als Übung überlassen.

Komplexität

O(1)O(1)O(n)

Formeller Beweis

Dem Leser als Übung überlassen.


8

O(n)O(n)

Durchlaufen Sie zunächst das Array von links nach rechts, und verwalten Sie dabei einen Stapel und ein Hilfsarray, das Ihnen für jedes Element den Index eines Elements angibt, das größer als dieses ist, sowie rechts davon.

1

Jedes Mal, wenn Sie ein neues Element im Array in Betracht ziehen, entfernen Sie es aus dem Stack, wenn dieses Element größer als das oberste Element des Stapels ist, und legen das Aux-Array-Element entsprechend dem obersten fest, unter dem sich der Index des neuen Elements befindet Berücksichtigung.

Entfernen Sie weitere Elemente aus dem Stapel und setzen Sie den entsprechenden Index, während das aktuelle Element größer ist. Sobald die Oberseite ein Element hat, das nicht kleiner ist (oder leer wird), schieben Sie das aktuelle Element auf den Stapel und fahren Sie mit dem nächsten Element des Arrays fort, wobei Sie den obigen Schritt wiederholen.

Machen Sie einen weiteren Durchgang (und ein anderes Aux-Array), aber gehen Sie von rechts nach links.

1

O(n)

ki

Der Pseudocode für den ersten Durchgang könnte folgendermaßen aussehen:

Stack <Pair<Elem, Index>> greats;
Elem auxArr[inputArr.Length];

for (Index i = 0; i < inputArr.Length; i++) {

    while (!greats.IsEmpty() && inputArr[i] > greats.PeekTop().Elem) {
        Pair top = greats.Pop();
        auxArr[top.Index] = i;
    }

    Pair p;
    p.Elem = inputArr[i];
    p.Index = i;

    greats.Push(p);
}

„Da Sie jedes Element des Arrays nur eine konstante Anzahl von Malen betrachten, ist dies O (n) -Zeit.“ Oh, Crums. Irgendwie hatte ich mehrere konstante Durchgänge ausgeschlossen und es als nicht O (n) verworfen. Sehr dumm. Ich bin dankbar für Ihre Erklärung, und ich werde noch einmal versuchen, es zu lösen.
Christopher Done
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.