Datenstruktur und Algorithmus II Algorithmusanalyse

1. Algorithmusanalyse

Wir haben bereits früher erwähnt, dass das ultimative Ziel der Erforschung von Algorithmen darin besteht, weniger Zeit und Speicher zu benötigen, um die gleichen Anforderungen zu erfüllen, und auch die Unterschiede im Zeit- und Platzverbrauch zwischen verschiedenen Algorithmen anhand von Fällen zu demonstrieren, aber wir können das nicht quantifizieren Zeit und Raum belegt, also werden wir als nächstes etwas über die Beschreibung und Analyse des Zeitverbrauchs des Algorithmus und des Raumverbrauchs des Algorithmus lernen. Die Analyse des Zeitverbrauchs des Algorithmus wird die Zeitkomplexitätsanalyse des Algorithmus genannt, und die Raumverbrauchsanalyse des Algorithmus wird die Raumkomplexitätsanalyse des Algorithmus genannt.

1.1 Zeitkomplexitätsanalyse des Algorithmus

Wir wollen den Zeitverbrauch des Algorithmus berechnen, zuerst müssen wir die Ausführungszeit des Algorithmus messen, also wie soll man sie messen?

Schätzmethode der nachträglichen Analyse:

Die einfachere Art, sich das vorzustellen, ist, dass wir den Algorithmus mehrmals ausführen und dann einen Timer zur Zeitmessung daneben nehmen. Diese Methode der Post-Event-Statistik sieht wirklich gut aus und erfordert nicht, dass wir wirklich einen Taschenrechner verwenden Nebenbei zu rechnen, denn die Computer bieten beide eine Zeitmessungsfunktion. Diese statistische Methode verwendet hauptsächlich den Computer-Timer, um die Laufzeit der von verschiedenen Algorithmen kompilierten Programme durch das entworfene Testprogramm und die Testdaten zu vergleichen, um die Effizienz des Algorithmus zu bestimmen, aber diese Methode hat große Mängel: Es ist notwendig das kompilierte Testprogramm basierend auf dem Algorithmus zu realisieren, was normalerweise viel Zeit und Energie kostet.Wenn sich nach Abschluss des Tests herausstellt, dass der Test ein sehr schlechter Algorithmus ist, ist alle vorherige Arbeit umsonst, und verschiedene Testumgebungen (Hardwareumgebungen) Auch die Testergebnisse unterscheiden sich sehr stark.

public static void main(String[] args) {
    
    
	long start = System.currentTimeMillis();
	int sum = 0;
	int n=100;
	for (int i = 1; i <= n; i++) {
    
    
		sum += i;
	}
	System.out.println("sum=" + sum);
	long end = System.currentTimeMillis();
	System.out.println(end-start);
}

Voranalyse und Schätzmethode:
Bevor das Computerprogramm geschrieben wird, wird der Algorithmus nach der statistischen Methode geschätzt.Nach der Zusammenfassung stellen wir fest, dass die Zeit, die ein in einer Hochsprache geschriebenes Programm benötigt, um auf dem Computer ausgeführt zu werden, davon abhängt folgende Faktoren:

  1. Vom Algorithmus angenommene Strategien und Lösungen;
  2. Die Qualität des durch die Kompilierung generierten Codes;
  3. Die Input-Skala des Problems (die sogenannte Problem-Input-Skala ist die Menge des Inputs);
  4. die Geschwindigkeit, mit der die Maschine Anweisungen ausführt;

Es ist ersichtlich, dass die Laufzeit eines Programms unabhängig von diesen Faktoren in Bezug auf Computerhardware und -software von der Qualität des Algorithmus und dem Eingabeumfang des Problems abhängt.

Wenn der Algorithmus festgelegt ist, hängt die Ausführungszeit des Algorithmus nur von der Eingabegröße des Problems ab.
Nehmen wir den vorherigen Summierungsfall erneut als Beispiel für die Analyse.

Anforderungen:
Berechne die Summe von 1 bis 100.
Die erste Lösung:

//如果输入量为n为1,则需要计算1次;
//如果输入量n为1亿,则需要计算1亿次;
public static void main(String[] args) {
    
    
	int sum = 0;//执行1次
	int n=100;//执行1次
	for (int i = 1; i <= n; i++) {
    
    //执行了n+1次
		sum += i;//执行了n次
	}
	System.out.println("sum=" + sum);
}

Die zweite Lösung:

//如果输入量为n为1,则需要计算1次;
//如果输入量n为1亿,则需要计算1次;
public static void main(String[] args) {
    
    
	int sum = 0;//执行1次
	int n=100;//执行1次
	sum = (n+1)*n/2;//执行1次
	System.out.println("sum="+sum);
}

Wenn die Eingabegröße n ist, wird daher der erste Algorithmus 1 + 1 + (n + 1) + n = 2n + 3 Mal ausgeführt, der zweite Algorithmus wird 1 + 1 + 1 = 3 Mal ausgeführt. Betrachten wir den Schleifenkörper des ersten Algorithmus als Ganzes und vernachlässigen die Beurteilung der Endbedingung, dann ist die Laufzeitdifferenz der beiden Algorithmen eigentlich die Differenz zwischen n und 1.

Warum wird die Schleifenbeurteilung in Algorithmus 1 n+1 Mal ausgeführt, was eine große Zahl zu sein scheint, aber ignoriert werden kann? Schauen wir uns das nächste Beispiel an:

Anforderung:
Berechnen Sie das Ergebnis von 100 1+100 2+100 3+...100 100
Code:

public static void main(String[] args) {
    
    
	int sum=0;
	int n=100;
	for (int i = 1; i <=n ; i++) {
    
    
		for (int j = 1; j <=n ; j++) {
    
    
			sum+=i;
		}
	}
	System.out.println("sum="+sum);
}

Wenn wir im obigen Beispiel genau untersuchen möchten, wie oft die Bedingung der Schleife ausgeführt wird, ist dies eine sehr mühsame Sache, und da der Code, der die Summe tatsächlich berechnet, der Schleifenkörper der inneren Schleife ist, beim Untersuchen der Effizienz des Algorithmus berücksichtigen wir nur die Ausführungszeiten des Kerncodes, was die Analyse vereinfacht.

Wir untersuchen die Komplexität des Algorithmus und konzentrieren uns auf eine Abstraktion (Gesetz) des Wachstums des Algorithmus, wenn die Eingabeskala weiter zunimmt, anstatt genau zu bestimmen, wie oft er ausgeführt werden muss, denn wenn dies der Fall ist, werden wir Neukompilierung Probleme wie langfristige Optimierung berücksichtigen müssen, ist es leicht, in die primäre und sekundäre zu fallen.

Wir kümmern uns nicht um die Sprache, die zum Schreiben des Programms verwendet wird, noch um die Art von Computer, auf dem diese Programme ausgeführt werden, wir kümmern uns nur um den Algorithmus, den es implementiert. Auf diese Weise ist es unabhängig von der Schrittweite des Schleifenindex und den Bedingungen des Schleifenabbruchs, der Variablendeklaration, der Ausgabe von Ergebnissen usw. bei der Analyse der Laufzeit des Programms das Wichtigste, das Programm als Algorithmus zu betrachten oder eine Reihe von Algorithmen, die von der Programmiersprache unabhängig sind. Wenn wir die Laufzeit eines Algorithmus analysieren, ist das Wichtigste, die Anzahl der Kernoperationen mit der Eingabeskala in Verbindung zu bringen.

Bildbeschreibung hier einfügen

1.1.1 Asymptotisches Wachstum von Funktionen

Konzept
Wenn zwei Funktionen f(n) und g(n) gegeben sind und es eine ganze Zahl N gibt, so dass f(n) für alle n>N immer größer als g(n) ist, dann sagen wir, dass f(n) asymptotisch schneller wächst als g(n).

Das Konzept scheint etwas schwierig zu verstehen, also machen wir als nächstes ein paar Tests.

Test 1:
Nehmen Sie an, dass die Eingabegrößen der vier Algorithmen alle n sind:

  1. Algorithmus A1 muss 2n+3 Operationen ausführen, was wie folgt zu verstehen ist: zuerst n Zyklen ausführen, nach Abschluss der Ausführung weitere n Zyklen und schließlich 3 Operationen;
  2. Algorithmus A2 benötigt 2n ​​Operationen;
  3. Algorithmus B1 muss 3n+1 Operationen ausführen, was wie folgt verstanden werden kann: zuerst n Schleifen ausführen, dann n Schleifen ausführen, dann n Schleifen ausführen und schließlich 1 Operation haben.
  4. Algorithmus B2 benötigt 3n Operationen;

Welcher der oben genannten Algorithmen ist also schneller?
Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen
Vergleichen Sie anhand der Datentabelle Algorithmus A1 und Algorithmus B1:
Wenn die Eingabeskala n = 1 ist, muss A1 fünfmal und B1 viermal ausgeführt werden, sodass die Effizienz von A1 geringer ist als die von B1;
wenn die Eingabeskala n=2, A1 Es muss 7 Mal ausgeführt werden und B1 muss 7 Mal ausgeführt werden, also ist die Effizienz von A1 die gleiche wie die von B1; wenn die Eingabegröße
n>2 die Anzahl der Ausführungen ist die von A1 benötigte Anzahl von Ausführungen ist immer kleiner als die von B1 benötigte Anzahl von Ausführungen, daher ist die Effizienz von A1 höher als die von B1 effizient;

Wir können also schlussfolgern:

Wenn die Eingabeskala n>2 ist, ist das asymptotische Wachstum von Algorithmus A1 kleiner als das von Algorithmus B1.
Durch Beobachtung des Liniendiagramms haben wir festgestellt, dass sich Algorithmus A1 und Algorithmus A2 allmählich überlappen, wenn die Eingabeskala zunimmt, und Algorithmus B1 und Algorithmus B1 überlappen sich allmählich.Algorithmus B2 überlappt sich allmählich zu einem Stück, so dass wir schließen,dass
mit zunehmender Eingabegröße der konstante Betrieb des Algorithmus vernachlässigbarist

Test 2:
Nehmen Sie an, dass die Eingabegrößen der vier Algorithmen alle n sind:

  1. Algorithmus C1 muss 4n+8 Operationen ausführen
  2. Algorithmus C2 muss n Operationen ausführen
  3. Algorithmus D1 muss 2n^2 Operationen ausführen
  4. Algorithmus D2 muss n^2 Operationen ausführen

Welcher der oben genannten Algorithmen ist also schneller?
Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen
Vergleichen Sie anhand der Datentabelle Algorithmus C1 und Algorithmus D1:
Wenn die Eingabeskala n<=3 ist, ist die Ausführungszeit von Algorithmus C1 länger als die von Algorithmus D1, sodass die Effizienz von Algorithmus C1 geringer ist; wenn die Eingabeskala n
> In 3 sind die Ausführungszeiten von Algorithmus C1 kürzer als von Algorithmus D1, daher ist Algorithmus D2 weniger effizient,
sodass Algorithmus C1 im Allgemeinen besser ist als Algorithmus D1.

Vergleichen und kontrastieren Sie im Liniendiagramm die Algorithmen C1 und C2:

Wenn die Eingabegröße zunimmt, überlappen sich Algorithmus C1 und Algorithmus C2 fast

Vergleichen Sie anhand des Liniendiagramms die C-Serie des Algorithmus und die D-Serie des Algorithmus:
Wenn die Eingabeskala zunimmt, ist die Häufigkeit der D-Serie viel höher als die von, selbst wenn der konstante Faktor vor n^2 entfernt wird die C-Serie.

Daraus kann geschlossen werden, dass
mit zunehmender Eingabegröße die Konstante multipliziert mit dem Term höchster Ordnung ignoriert werden kann

Test 3:
Angenommen, die Eingabeskala der vier Algorithmen ist n:
Algorithmus E1: 2n^2+3n+1
Algorithmus E2: n^2
Algorithmus F1: 2n^3+3n+1
Algorithmus F2: n^3
Dann das Obige Algorithmus, was ist schneller?

Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen
Vergleichen Sie anhand der Datentabelle Algorithmus E1 und Algorithmus F1:
Wenn n=1, ist die Anzahl der Ausführungen von Algorithmus E1 und Algorithmus F1 gleich;
wenn n>1, ist die Anzahl von Ausführungen von Algorithmus E1 weit geringer als die Anzahl von Ausführungen von Algorithmus F1,
also Algorithmus E1 Insgesamt liegt es an Algorithmus F1.

Durch das Liniendiagramm können wir sehen, dass die Algorithmus-F-Reihe mit der Erhöhung von n speziell wird und die Algorithmus-E-Reihe mit der Erhöhung von n im Vergleich zum Algorithmus F langsamer wird, sodass gefolgert werden kann, dass die höchste
The Index des Elements ist groß, und wenn n wächst, wächst auch das Ergebnis sehr schnell

Test 4:
Angenommen, die Eingabegröße der fünf Algorithmen ist n:
Algorithmus G: n^3;
Algorithmus H: n^2;
Algorithmus I: n
Algorithmus J: logn
Algorithmus K: 1

Welcher der oben genannten Algorithmen ist also effizienter?
Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen
Durch Betrachten der Datentabellen und Liniendiagramme ist es leicht, eine Schlussfolgerung zu ziehen:
Je kleiner die höchste Potenz von n in der Algorithmusfunktion ist, desto höher ist die Algorithmuseffizienz

Wenn wir das Wachstum des Algorithmus mit der Eingabeskala vergleichen, können zusammenfassend die folgenden Regeln befolgt werden:

  1. Konstanten in algorithmischen Funktionen können ignoriert werden;
  2. Der konstante Faktor der höchsten Potenz in der Algorithmusfunktion kann vernachlässigt werden;
  3. Je kleiner die höchste Potenz in der Algorithmusfunktion ist, desto höher ist die Algorithmuseffizienz.

1.1.2 Zeitkomplexität des Algorithmus

1.1.2.1 Big-O-Notation

Definition

Bei der Analyse des Algorithmus ist die Gesamtausführungszeit T(n) der Anweisung eine Funktion der Problemgröße n, und dann wird die Variation von T(n) mit n analysiert und die Größe von T(n) bestimmt. Die Zeitkomplexität des Algorithmus ist das Zeitmaß des Algorithmus, bezeichnet als: T(n) = O(f(n)). Dies bedeutet, dass mit zunehmender Problemgröße n die Wachstumsrate der Algorithmusausführungszeit gleich der Wachstumsrate von f(n) ist, was als asymptotische Zeitkomplexität des Algorithmus oder kurz Zeitkomplexität bezeichnet wird, wobei f (n) ist die Problemgröße eine Funktion von n.

Hier müssen wir eines klarstellen: Anzahl der Ausführungen = Ausführungszeit
Verwenden Sie das große O(), um die Notation der Zeitkomplexität des Algorithmus widerzuspiegeln, die wir die große O-Notation nennen. Im Allgemeinen ist mit zunehmender Eingabegröße n der Algorithmus mit dem langsamsten Wachstum von T(n) der optimale Algorithmus.

Im Folgenden verwenden wir die große O-Notation, um die Zeitkomplexität einiger Summationsalgorithmen auszudrücken:

Algorithmus eins

public static void main(String[] args) {
    
    
	int sum = 0;//执行1次
	int n=100;//执行1次
	sum = (n+1)*n/2;//执行1次
	System.out.println("sum="+sum);
}

Algorithmus zwei

public static void main(String[] args) {
    
    
	int sum = 0;//执行1次
	int n=100;//执行1次
	for (int i = 1; i <= n; i++) {
    
    
		sum += i;//执行了n次
	}
	System.out.println("sum=" + sum);
}

Algorithmus drei

public static void main(String[] args) {
    
    
	int sum=0;//执行1次
	int n=100;//执行1次
	for (int i = 1; i <=n ; i++) {
    
    
		for (int j = 1; j <=n ; j++) {
    
    
			sum+=i;//执行n^2次
		}
	}
	System.out.println("sum="+sum);
}

Wenn die Ausführungszeiten der Beurteilungsbedingung und die Ausführungszeiten der Ausgabeanweisung ignoriert werden, dann sind die Ausführungszeiten der obigen Algorithmen bei einer Eingabegröße von n: Algorithmus 1: 3 mal
Algorithmus
2: n+3 mal
Algorithmus 3 : n^2+2 Zweitklassig

Wenn die Zeitkomplexität jedes der oben genannten Algorithmen in großer O-Notation ausgedrückt wird, wie sollte sie ausgedrückt werden? Basierend auf unserer Analyse des asymptotischen Wachstums der Funktion können die folgenden Regeln verwendet werden, um die Darstellung der großen O-Ordnung abzuleiten:

  1. Alle additiven Konstanten zur Laufzeit durch Konstante 1 ersetzen;
  2. In der modifizierten Anzahl von Läufen werden nur Terme höherer Ordnung beibehalten;
  3. Wenn der Term höchster Ordnung existiert und der konstante Faktor nicht 1 ist, entfernen Sie die mit diesem Term multiplizierte Konstante;

Daher sind die großen O-Notationen der obigen Algorithmen:
Algorithmus 1: O(1)
Algorithmus 2: O(n)
Algorithmus 3: O(n^2)

1.1.2.2 Gemeinsame Big-O-Order

1. Lineare Ordnung
enthält im Allgemeinen nicht verschachtelte Schleifen mit linearer Ordnung. Die lineare Ordnung bedeutet, dass bei Erweiterung der Eingabeskala die entsprechende Anzahl von Berechnungen linear zunimmt, zum Beispiel:

public static void main(String[] args) {
    
    
	int sum = 0;
	int n=100;
	for (int i = 1; i <= n; i++) {
    
    
		sum += i;
	}
	System.out.println("sum=" + sum);
}

Für den obigen Code ist die Zeitkomplexität seiner Schleife O(n), da der Code im Schleifenkörper n-mal ausgeführt werden muss

2. Quadratische Ordnung
Allgemeine verschachtelte Schleifen gehören zu dieser Zeitkomplexität

public static void main(String[] args) {
    
    
	int sum=0,n=100;
	for (int i = 1; i <=n ; i++) {
    
    
		for (int j = 1; j <=n ; j++) {
    
    
			sum+=i;
		}
	}
	System.out.println(sum);
}

Im obigen Code ist n = 100, das heißt, jedes Mal, wenn die äußere Schleife ausgeführt wird, wird die innere Schleife 100 Mal ausgeführt. Wenn das Programm aus diesen beiden Schleifen herauskommen möchte, muss es 100 * 100 Mal ausgeführt werden Es ist das Quadrat von n, also ist die Zeitkomplexität dieses Codes O(n^2).

3. Kubische Ordnung
Zu dieser Zeitkomplexität gehören im Allgemeinen dreistufige verschachtelte Schleifen

public static void main(String[] args) {
    
    
	int x=0,n=100;
	for (int i = 1; i <=n ; i++) {
    
    
		for (int j = i; j <=n ; j++) {
    
    
			for (int j = i; j <=n ; j++) {
    
    
				x++;
			}
		}
	}
	System.out.println(x);
}

Im obigen Code ist n=100, das heißt, jedes Mal, wenn die äußere Schleife ausgeführt wird, wird die mittlere Schleife 100 Mal ausgeführt, und jedes Mal, wenn die mittlere Schleife ausgeführt wird, muss die innerste Schleife 100 Mal ausgeführt werden Von den drei Schleifen muss es 100100100 Mal ausgeführt werden, was die dritte Potenz von n ist, also ist die Zeitkomplexität dieses Codes O (n ^ 3).

4. Logarithmische Ordnung
Logarithmus gehört zu den Inhalten der Oberstufenmathematik Unser Analyseprogramm basiert hauptsächlich auf Programmen und wird durch Mathematik ergänzt, also keine Sorge.

int i=1,n=100;
while(i<n){
    
    
	i = i*2;
}

Da es jedes Mal nach i*2 einen Schritt näher an n ist, wird die Schleife unter der Annahme, dass x 2s multipliziert werden, um größer als n zu sein, verlassen. Da es 2^x=n ist, wird x=log(2)n erhalten, also ist die Zeitkomplexität dieses Zyklus O(logn);

Für die logarithmische Ordnung gilt: Wenn die Eingabeskala n zunimmt, ist ihr Wachstumstrend unabhängig von der Basis gleich, also ignorieren wir die Basis.

Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen
5. Konstante Ordnung
Im Allgemeinen sind diejenigen, die keine Schleifenoperationen beinhalten, konstante Ordnungen, da sie die Anzahl der Operationen nicht erhöhen, wenn n wächst. Zum Beispiel:

public static void main(String[] args) {
    
    
	int n=100;
	int i=n+2;
	System.out.println(i);
}

Der obige Code wird unabhängig von der Eingabegröße n zweimal ausgeführt. Gemäß der großen O-Ableitungsregel wird die Konstante durch 1 ersetzt, sodass die Zeitkomplexität des obigen Codes O (1) ist.

Hier ist eine Zusammenfassung gängiger Zeitkomplexitäten:

Bildbeschreibung hier einfügen
Ihre Komplexität, von niedrig bis hoch, ist:

 O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)

Gemäß der vorherigen Liniendiagrammanalyse werden wir feststellen, dass der Zeitaufwand ausgehend von der quadratischen Reihenfolge mit zunehmender Eingabeskala stark ansteigt. Daher verfolgt unser Algorithmus O(1), O(logn), O(n) , O(nlogn) diese Arten von Zeitkomplexität, und wenn sich herausstellt, dass die Zeitkomplexität des Algorithmus quadratisch, kubisch oder noch komplexer ist, dann können wir diesen Algorithmus aufteilen, ist nicht ratsam, muss optimiert werden.

1.1.2.3 Zeitkomplexitätsanalyse des Funktionsaufrufs

Zuvor haben wir die Zeitkomplexität des Algorithmuscodes in einer einzelnen Funktion analysiert, und dann haben wir die Zeitkomplexität während des Funktionsaufrufs analysiert.

Fall Nummer eins:

public static void main(String[] args) {
    
    
	int n=100;
	for (int i = 0; i < n; i++) {
    
    
		show(i);
	}
}

private static void show(int i) {
    
    
	System.out.println(i);
}

In der main-Methode gibt es eine for-Schleife, und der Schleifenrumpf ruft die show-Methode auf Da innerhalb der show-Methode nur eine Codezeile ausgeführt wird, beträgt die Zeitkomplexität der show-Methode O(1) und die Zeitkomplexität der Hauptmethode ist O( n)

Fall zwei:

public static void main(String[] args) {
    
    
	int n=100;
	for (int i = 0; i < n; i++) {
    
    
		show(i);
	}
}

private static void show(int i) {
    
    
	for (int j = 0; j < i; i++) {
    
    
		System.out.println(i);
	}
}

In der main-Methode gibt es eine for-Schleife und der Schleifenrumpf ruft die show-Methode auf Da es auch eine for-Schleife innerhalb der show-Methode gibt, ist die Zeitkomplexität der show-Methode O(n) und die Zeitkomplexität of Die Hauptmethode ist O(n ^2)

Fall drei:

public static void main(String[] args) {
    
    
	int n=100;
	show(n);
	for (int i = 0; i < n; i++) {
    
    
		show(i);
	}
	for (int i = 0; i < n; i++) {
    
    
		for (int j = 0; j < n; j++) {
    
    
			System.out.println(j);
		}
	}
}

private static void show(int i) {
    
    
	for (int j = 0; j < i; i++) {
    
    
		System.out.println(i);
	}
}

In der show-Methode gibt es eine for-Schleife, also ist die Zeitkomplexität der show-Methode O(n).In der main-Methode ist die Anzahl der internen Ausführungen der Codezeile show(n) n, und die erste for-Schleife ruft show-Methode auf, ihre Ausführungszeit beträgt also n^2, nur eine Codezeile wird in der zweiten verschachtelten for-Schleife ausgeführt, ihre Ausführungszeit beträgt also n^2, dann beträgt die Gesamtausführungszeit der Hauptmethode n+n^2+n^2=2n^2+n. Entfernen Sie gemäß den Ableitungsregeln für große O n, um das Element höchster Ordnung beizubehalten, und entfernen Sie den konstanten Faktor 2 des Elements höchster Ordnung, sodass die endgültige Zeitkomplexität der Hauptmethode O (n ^ 2) ist.

1.1.2.4 Worst-Case

Aus psychologischer Sicht hat jeder eine Erwartungshaltung, wenn man zum Beispiel ein halbes Glas Wasser sieht, sagt man: Wow, da ist noch ein halbes Glas Wasser! Aber manche Leute werden sagen: Gott, es gibt nur ein halbes Glas Wasser. Die meisten Menschen sind besorgt über zukünftige Misserfolge und neigen dazu, mit dem Schlimmsten zu rechnen, sodass die Parteien selbst dann psychologisch vorbereitet sind, wenn das schlimmste Ergebnis eintritt, und es leichter ist, das Ergebnis zu akzeptieren. Wenn das Schlimmste nicht eintritt, werden die Parteien sehr glücklich sein.

Die Algorithmenanalyse ist ähnlich.Wenn es eine Anforderung gibt:
Es gibt ein Array, das n Zufallszahlen speichert, suchen Sie bitte die angegebene Zahl daraus.

public int search(int num){
    
    
	int[] arr={
    
    11,10,8,9,7,22,23,0};
	for (int i = 0; i < arr.length; i++) {
    
    
		if (num==arr[i]){
    
    
			return i;
	    }
	}
	return -1;
}

Bester Fall:
Die erste Zahl, die Sie nachschlagen, ist die erwartete Zahl, dann ist die Zeitkomplexität des Algorithmus O (1)

Worst Case:
Die letzte zu findende Zahl ist die erwartete Zahl, dann ist die Zeitkomplexität des Algorithmus O(n)

Durchschnittlicher Fall:
Die durchschnittlichen Kosten einer numerischen Suche sind O(n/2)

Der Worst Case stellt eine Garantie dar. In der Anwendung stellt dies die grundlegendste Garantie dar. Selbst im Worst Case kann der Dienst normal erbracht werden. Daher bezieht sich die von uns genannte Laufzeit, sofern nicht anders angegeben, auf die Worst Case Laufzeit.

1.2 Raumkomplexitätsanalyse des Algorithmus

Computerhardware und -software haben eine relativ lange Evolutionsgeschichte hinter sich, insbesondere als Speicher, der eine Umgebung für die Datenverarbeitung bereitstellt.Von den früheren 512k über 1M, 2M, 4M usw. bis hin zu den aktuellen 8G und sogar 16G und 32G, so ist in der Anfangszeit auch der Speicherverbrauch des Algorithmus im Betrieb ein häufig zu berücksichtigendes Problem. Wir können die Raumkomplexität des Algorithmus verwenden, um die Speichernutzung des Algorithmus zu beschreiben.

1.2.1 Gemeinsame Speichernutzung in Java

1. Grundlegende Datentyp-Speichernutzung:
Bildbeschreibung hier einfügen

2. Die Art und Weise, wie ein Computer auf den Speicher zugreift, ist Byte für Byte
Bildbeschreibung hier einfügen
. 3. Eine Referenz (Maschinenadresse) benötigt 8 Bytes zur Darstellung:
zum Beispiel: Date date = new Date(), dann muss die Variable date 8 Bytes zur Darstellung belegen

4. Erstellen Sie ein Objekt, wie beispielsweise new Date().Zusätzlich zu dem Speicher, der von den Daten belegt wird, die im Date-Objekt gespeichert sind (wie Jahr, Monat, Tag usw.), hat das Objekt selbst auch einen Speicher-Overhead jedes Objekts ist 16 Byte groß und wird verwendet, um die Header-Informationen des Objekts zu speichern.

5. Die Verwendung des allgemeinen Speichers, wenn er weniger als 8 Bytes beträgt, wird er automatisch auf 8 Bytes aufgefüllt:
Bildbeschreibung hier einfügen
6. Arrays in Java sind auf Objekte beschränkt, und sie benötigen im Allgemeinen zusätzlichen Speicher aufgrund der Datensatzlänge, primitiver Daten Typ Ein Array von s benötigt typischerweise 24 Bytes Header-Informationen (16 Overhead für das eigene Objekt, 4 Bytes für die Länge und 4 Füllbytes) plus den Speicher, der benötigt wird, um den Wert zu halten.

1.2.2 Raumkomplexität des Algorithmus

Das Verständnis des grundlegendsten Mechanismus des Java-Speichers kann uns effektiv dabei helfen, die Speichernutzung einer großen Anzahl von Programmen abzuschätzen.
Die Berechnungsformel der Platzkomplexität des Algorithmus wird festgehalten als: S(n) = O(f(n)), wobei n die Eingangsgröße und f(n) die Funktion der Aussage über den belegten Speicherplatz ist von n.

Fall:
Kehrt die angegebenen Array-Elemente um und gibt den umgekehrten Inhalt zurück.
Lösung eins:

public static int[] reverse1(int[] arr){
    
    
	int n=arr.length;//申请4个字节
	int temp;//申请4个字节
	for(int start=0,end=n-1;start<=end;start++,end--){
    
    
		temp=arr[start];
		arr[start]=arr[end];
		arr[end]=temp;
	}
	return arr;
}

Lösung zwei:

public static int[] reverse2(int[] arr){
    
    
	int n=arr.length;//申请4个字节
	int[] temp=new int[n];//申请n*4个字节+数组自身头信息开销24个字节
	for (int i = n-1; i >=0; i--) {
    
    
		temp[n-1-i]=arr[i];
	}
	return temp;
}

Wenn wir den von der Beurteilungsbedingung belegten Speicher ignorieren, erhalten wir die Speichernutzung wie folgt:

Algorithmus 1:
Unabhängig von der Größe des eingehenden Arrays immer zusätzlich 4+4=8 Bytes beantragen;

Algorithmus 2:
4+4n+24=4n+28;

Gemäß der großen O-Ableitungsregel ist die Raumkomplexität von Algorithmus 1 O(1) und die Raumkomplexität von Algorithmus 2 O(n), sodass Algorithmus 1 aus Sicht der Raumbelegung besser ist als Algorithmus 2.

Da es in Java einen Speicher-Garbage-Collection-Mechanismus gibt und jvm auch die Speichernutzung von Programmen optimiert (z. B. Just-in-Time-Kompilierung), können wir die Speichernutzung eines Java-Programms nicht genau auswerten, aber die grundlegende Speichernutzung kennen java ermöglicht uns die Speichernutzung des Java-Programms wird geschätzt.

Da der Speicher aktueller Computergeräte im Allgemeinen relativ groß ist, beginnen Personal Computer grundsätzlich mit 8 G, und die großen können 32/64 G erreichen, sodass die Speichernutzung im Allgemeinen nicht der Flaschenhals unseres Algorithmus ist.Im Allgemeinen beziehen wir uns direkt auf die Komplexität: Der Standardwert ist die Zeitkomplexität des Algorithmus .

Wenn das Programm, das Sie ausführen, jedoch eine eingebettete Entwicklung ist, insbesondere das integrierte Programm auf einigen Sensorgeräten, da der Speicher dieser Geräte sehr klein ist, im Allgemeinen einige kb, besteht eine Anforderung an die Platzkomplexität des Algorithmus bei Diesmal, aber im Allgemeinen Diejenigen, die Java-Entwicklung machen, sind im Grunde Server-Entwicklung, und im Allgemeinen gibt es kein solches Problem.

Je suppose que tu aimes

Origine blog.csdn.net/qq_33417321/article/details/121945595
conseillé
Classement