2008-10-20 16 views
11

Ich benutze Pseudo-Code hier, aber das ist in JavaScript. Mit dem möglichst effizienten Algorithmus versuche ich, die Höhe und den Tiefpunkt bei einer Reihe positiver Ganzzahlen zu finden. Das ist es, was ich mir ausgedacht habe, aber ich denke nicht, dass es das beste ist, und ich habe mich nur gefragt, ob jemand andere Vorschläge hat.Bester Algorithmus zur Bestimmung von hoch und niedrig in einer Reihe von Zahlen?

var low = 1; 
var high = 1; 
for (loop numbers) { 
    if (number > high) { 
     high = number; 
    } 
    if (low == 1) { 
     low = high; 
    } 
    if (number < low) { 
     low = number; 
    } 
} 

Antwort

28

initialisieren Sie die hohen und niedrigen, um das erste Element zu sein. macht viel mehr Sinn, als eine willkürlich "hohe" oder "niedrige" Zahl auszuwählen.

var myArray = [...], 
    low = myArray[0], 
    high = myArray[0] 
; 
// start looping at index 1 
for (var i = 1, l = myArray.length; i < l; ++i) { 
    if (myArray[i] > high) { 
     high = myArray[i]; 
    } else if (myArray[i] < low) { 
     low = myArray[i]; 
    } 
} 

oder die Notwendigkeit, die Vermeidung der Array mehrere Male Nachschlag:

for (var i = 1, val; (val = myArray[i]) !== undefined; ++i) { 
    if (val > high) { 
     high = val; 
    } else if (val < low) { 
     low = val; 
    } 
} 
+0

Wenn Sie mit dem ersten Element initialisieren, kümmern Sie sich nicht mehr um die Grenzen Ihres Datentyps. – Mnebuerquo

+0

Initialisieren mit dem ersten Element, Sie nehmen an, dass es eins gibt. (Sequenz könnte leer sein. –

+0

das ist ok. In Javascript, werden Sie niedrig = undefiniert, und hoch = undefiniert. ... macht viel mehr Sinn als negative Unendlichkeit ... – nickf

-6

In Python:

>>> seq = [1, 2, 3, 4, 5, 6, 7] 
>>> max(seq) 
7 
>>> min(seq) 
1 
+0

Seine Sprache ist Javascript – jjnguy

+0

gut, es ist nicht, dass er an JS interessiert war, es ist, dass er nach einem Algorithmus suchte. Das ist wirklich keine nützliche Antwort. – nickf

+0

Sie werden feststellen, dass meine Antwort völlig witzig ist. Collections.sort ist nicht die Antwort auf "Bester Sortieralgorithmus?" auch wenn wir Java sprechen. –

9

Sie haben es in O(n) Zeit zu tun, weil Sie eine Schleife durch alle brauchen (n) der Elemente, sie zu überprüfen, weil jeder eines der Elemente der sein kann, min oder max. (Es sei denn, sie sind bereits sortiert.)

Mit anderen Worten, Sie müssen alle Elemente durchlaufen und die Max- und Min-Überprüfung wie Sie haben.

Sortierung ist in der Regel bestenfalls O(n*log(n)). Somit ist es langsamer als ein einzelner Durchlauf (O(n)).

0

Angenommen, die Liste ist nicht bereits sortiert, das ist das Beste, was Sie tun können. Sie können sich selbst einen Vergleich speichern die folgenden (in Pseudo-Code), indem Sie:

low = +INFINITY 
high = -INFINITY 
for each n in numbers: 
    if n < low: 
     low = n 
    if n > high: 
     high = n 

Dies ist ein O (n) -Operation, die im Grunde ist das Beste, was Sie tun können.

+0

Das ist eine andere Art, es zu tun. Aber das macht diesen Algorithmus kaum falsch *, da ein "falscher" Algorithmus entweder (a) eine falsche Antwort zurückgeben würde, oder (b) eine höhere Komplexität haben würde. – mipadi

+0

Ja, du hast Recht. Mein Fehler. Ich dachte, dass der Algorithmus die falsche Antwort zurückgeben würde, weil ich Ihre if-Bedingungen falsch verstanden habe. Es tut uns leid. –

8

Ihr Beispiel ist so ziemlich das effizienteste Algorithmus, aber offensichtlich wird es nicht funktionieren, wenn alle die Zahlen weniger sind

var low = numbers[0]; // first number in array 
var high = numbers[0]; // first number in array 
for (loop numbers) { 
    if (number > high) { 
     high = number; 
    } 
    if (number < low) { 
     low = number; 
    } 
} 
+0

es gibt keine Möglichkeit, dass die Zahl größer als hoch UND weniger als niedrig sein kann, so dass Sie die beiden If-Anweisungen in ein If/Else kombinieren können. – nickf

+0

der Wortlaut könnte geklärt werden. er meint, wenn alle Zahlen <1 sind oder wenn alle Zahlen> 1 sind, ist die bereitgestellte Quelle in jedem Fall gut. – DarenW

7

Wenn die Liste klein ist (wobei „klein“ ist weniger als ein paar tausend el: als 1 oder größer als 1 Dieser Code in diesen Fällen funktionieren Sie tun es nicht viel (wo "viel" weniger als ein paar tausend Mal ist) spielt keine Rolle. Profilieren Sie Ihren Code zuerst, um den echten Engpass zu finden, bevor Sie sich Gedanken über die Optimierung Ihrer Max/Min-Algorithmen machen.

Nun zur Beantwortung der Frage, die Sie gestellt haben.

Da es keine Möglichkeit gibt, jedes Element der Liste zu betrachten, ist eine lineare Suche der effizienteste Algorithmus. Es dauert N Zeit, wobei N die Anzahl der Elemente in der Liste ist. Alles in einer Schleife zu tun ist effizienter als das Aufrufen von max() und dann min() (was 2 * N Zeit benötigt). Ihr Code ist also grundsätzlich korrekt, auch wenn negative Zahlen nicht berücksichtigt werden. Hier ist es in Perl.

# Initialize max & min 
my $max = $list[0]; 
my $min = $list[0]; 
for my $num (@list) { 
    $max = $num if $num > $max; 
    $min = $num if $num < $min; 
} 

Sortieren und dann greifen das erste und letzte Element ist am wenigsten effizient. Es dauert N * log (N) wobei N die Anzahl der Elemente in der Liste ist.

Der effizienteste Min/Max-Algorithmus ist einer, bei dem min/max jedes Mal neu berechnet wird, wenn ein Element hinzugefügt oder aus der Liste entfernt wird.In der Tat das Ergebnis zwischenspeichern und eine lineare Suche jedes Mal vermeiden. Die dafür benötigte Zeit ist die Anzahl der Änderungen der Liste. Es dauert höchstens M Zeit, wobei M die Anzahl der Änderungen ist, egal wie oft Sie es nennen.

Um dies zu tun, könnten Sie einen Suchbaum betrachten, der seine Elemente in der richtigen Reihenfolge hält. Das Min/Max in dieser Struktur zu erhalten, ist O (1) oder O (log [n]) abhängig davon, welchen Baumstil Sie verwenden.

1

Die einzige weitere Optimierung, die ich vorschlagen würde, ist die Optimierung der Schleife selbst. Es ist schneller zu zählen als in JavaScript zu zählen.

+0

Das ist eine sehr überraschende Sache zu lernen ... Auf einer Suche wird dies von http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=6 auch bestätigt ... Warum geschieht dies und tut Dies übertragen auf andere Sprachen? – sundar

+0

In zweiter Linie denke ich, ich kann den Grund erraten. In einer Aufwärtszählschleife müssen wir die obere Schranke holen und mit dem Iteratorindex vergleichen (mov reg1, Wert; cmp reg2, reg1; jne addr;), während wir in einer Abwärtszählschleife eine interne Anweisung für verwenden können Vergleich mit Null (jnz reg2, addr). Ist es das? – sundar

+0

das ist es, soweit ich weiß – Gene

4

Obwohl es immer noch ein O (n) -Algorithmus ist, können Sie es 25% schneller machen (das heißt, die Proportionalitätskonstante ist 3/2 vs 2), indem Sie zuerst paarweise paarweise miteinander vergleichen und dann kleiner mit min und größer bis max. Ich weiß nicht, Javascript, aber hier ist es in C++:

std::pair<int, int> minmax(int* a, int n) 
{ 
    int low = std::numeric_limits<int>::max(); 
    int high = std::numeric_limits<int>::min(); 

    for (int i = 0; i < n-1; i += 2) { 
    if (a[i] < a[i+i]) { 
     if (a[i] < low) { 
     low = a[i]; 
     } 
     if (a[i+1] > high) { 
     high = a[i+1]; 
     } 
    } 
    else { 
     if (a[i] > high) { 
     high = a[i]; 
     } 
     if (a[i+1] < low) { 
     low = a[i+1]; 
     } 
    } 
    } 

    // Handle last element if we've got an odd array size 
    if (a[n-1] < low) { 
    low = a[n-1]; 
    } 
    if (a[n-1] > high) { 
    high = a[n-1]; 
    } 

    return std::make_pair(low, high); 
} 
+2

Das setzt voraus, dass die Komplexität des zusätzlichen Codes (aufgrund der zusätzlichen Verzweigungen) die Dinge nicht verlangsamt. Oh, und O (3n/2) == O (2n) == O (n) - Sie müssen etwas anderes als Groß-O-Notation verwenden, um einen konstanten Faktorunterschied zu messen. – TimB

+0

Vereinbart, dass O (3n/2) == O (n), ich habe nur darauf hingewiesen, dass der konstante Faktor tatsächlich kleiner ist als bei der "offensichtlichen" Implementierung. Dies ist der Lehrbuch-CLR-Algorithmus für Simultane Min/Max, und ist tatsächlich schneller für wirklich große n. Normalerweise macht es aber keinen Unterschied ... :) –

+0

BTW, beachten Sie, dass Sie zuerst zwei paarweise drei Vergleiche für jeweils zwei Elemente durchführen, und zwar jeweils zwei für jeweils zwei (dh 4 für jede 2), wie Sie es mit dem einfache Version. Es sieht so aus, als ob es sich mehr verzweigt, aber es ist nicht so, weil der Schritt größer ist. –

3
var numbers = [1,2,5,9,16,4,6]; 

var maxNumber = Math.max.apply(null, numbers); 
var minNumber = Math.min.apply(null, numbers); 
+0

In der Tat ist dies kein Algorithmus, es ist nur eine Möglichkeit, Min/Max-Wert in einem Array zu finden. Dies kann der effizienteste Weg sein oder nicht - hängt vom tatsächlichen Math.min()/Math.max() - Algorithmus ab, der von einem gegebenen JavaScript-Engine-Anbieter implementiert wird. In Spidermonkey: http://mxr.mozilla.org/mozilla/source/js/src/jsmath.c – pawel

+0

Das ist brilliant. Ich wusste nicht, dass diese Methoden n Parameter verwendeten. Gut gemacht. – fearphage

3

Nickf Algorithmus nicht der beste Weg, dies zu tun. Im schlimmsten Fall vergleicht der nickf-Algorithmus 2 Zahlen pro Zahl, für eine Gesamtzahl von 2n - 2.

Wir können ein bisschen besser machen. Wenn Sie zwei Elemente a und b vergleichen, wissen wir, wenn a> b ist, dass a nicht das Min ist und b nicht das Maximum ist. Auf diese Weise nutzen wir alle verfügbaren Informationen, um so viele Elemente wie möglich zu eliminieren. Nehmen Sie zur Vereinfachung an, dass wir eine gerade Anzahl von Elementen haben.

sie in Paare brechen: (a1, a2), (a3, a4) usw.

vergleichen sie, so dass sie in eine Reihe von Gewinnern und Verlierern zu brechen - das dauert n/2 vergleicht, uns zwei geben Sätze der Größe n/2. Finden Sie nun das Maximum der Gewinner und das Minimum der Verlierer.

Von oben, die Suche nach dem Minimum oder Maximum von n Elementen dauert n-1 Vergleiche. Somit ist die Laufzeit: n/2 (für die ersten Vergleiche) + n/2 - 1 (max der Gewinner) + n/2 - 1 (min der Verlierer) = n/2 + n/2 + n/2 -2 = 3N/2 - 2. Wenn n ungerade ist, haben wir ein weiteres Element in jedem der Sätze, so wird die Laufzeit 3N/2

in der Tat, können wir beweisen, dass dies der schnellste, dass Dieses Problem kann möglicherweise durch irgendeinen Algorithmus gelöst werden.

Ein Beispiel:

Angenommen unsere Array 1, 5, 2, 3, 1, 8, 4 Dividieren in Paare: (1,5), (2,3) (1,8), (4, -). Vergleichen. Die Gewinner sind: (5, 3, 8, 4). Die Verlierer sind (1, 2, 1, 4).

die Gewinner Scannen gibt 8. die Verlierer Scannen gibt 1.

2

diese Schnipsel Ausprobieren für Echt auf V8, Drew Hall-Algorithmus in 2/3 der Zeit von Nickf der läuft, wie vorhergesagt. Wenn Sie die Schleife statt nach oben herunterzählen, wird sie auf etwa 59% der Zeit reduziert (obwohl dies mehr von der Implementierung abhängig ist). Nur leicht getestet:

var A = [ /* 100,000 random integers */]; 

function minmax() { 
    var low = A[A.length-1]; 
    var high = A[A.length-1]; 
    var i, x, y; 
    for (i = A.length - 3; 0 <= i; i -= 2) { 
     y = A[i+1]; 
     x = A[i]; 
     if (x < y) { 
      if (x < low) { 
       low = x; 
      } 
      if (high < y) { 
       high = y; 
      } 
     } else { 
      if (y < low) { 
       low = y; 
      } 
      if (high < x) { 
       high = x; 
      } 
     } 
    } 
    if (i === -1) { 
     x = A[0]; 
     if (high < x) { 
      high = x; 
     } else if (x < low) { 
      low = x; 
     } 
    } 
    return [low, high]; 
} 

for (var i = 0; i < 1000; ++i) { minmax(); } 

Aber Mann, es ist ziemlich hässlich.

2

Javascript-Arrays haben eine native Sortierfunktion, die eine Funktion für den Vergleich akzeptiert. Sie können die Zahlen sortieren und einfach den Kopf und den Schwanz nehmen, um das Minimum und Maximum zu erhalten.

var sorted = arrayOfNumbers.sort(function(a, b) { return a - b; }), 
    ,min = sorted[0], max = sorted[sorted.length -1]; 

standardmäßig sortiert die Sortiermethode lexikographisch (lexikalische Reihenfolge) ist also, warum Sie in einer Funktion zu übergeben müssen sie numerische Sortierung zu erhalten zu verwenden. Die übergebene Funktion muss 1, -1 oder 0 zurückgeben, um die Sortierreihenfolge zu bestimmen.

Im Fall von Zahlen können Sie lediglich die zweite von der ersten Größe abziehen, um die Reihenfolge zu bestimmen.

var numbers = [5,8,123,1,7,77,3.14,-5]; 

// default lexicographical sort 
numbers.sort() // -5,1,123,3.14,5,7,77,8 

// numerical sort 
numbers.sort(function(a, b) { return a - b; }) // -5,1,123,3.14,5,7,77,8 
+1

+1: Sie scheinen der einzige zu sein, der das richtig behandelt (ein Sortierproblem). – KooiInc

+0

@KooiInc Das Sortieren des Arrays führt mehr als nötig aus. Das Sortieren erfordert O (log n) -Vergleiche, während das Finden der größten und kleinsten Zahlen O (n) -Vergleiche erfordert. – Amok

0

dieser Algorithmus für O arbeitet (n) und nicht mehr zusätzlichen Speicher benötigt Elemente speichern ...

enter code here 
int l=0,h=1,index,i=3; 
    if(a[l]>a[h]) 
       swap(&a[l],&a[h]); 
    for(i=2;i<9;i++) 
    { 
           if(a[i]<a[l]) 
           { 
             swap(&a[i],&a[l]); 
           } 
           if(a[i]>a[h]) 
           { 
              swap(&a[i],&a[h]); 
           } 
    } 
    printf("Low: %d High: %d",a[0],a[1]); 
0

es die ES6 Weise tun mit spread syntax:

var arrNums = [1, 2, 3, 4, 5]; 
Math.max(...arrNums) // 5 
Math.min(...arrNums) // 1 
Verwandte Themen