Gibt es O (1 / n) -Algorithmen?
Oder irgendetwas anderes, das kleiner als O (1) ist?
Gibt es O (1 / n) -Algorithmen?
Oder irgendetwas anderes, das kleiner als O (1) ist?
Antworten:
Diese Frage ist nicht so dumm, wie es scheinen mag. Zumindest theoretisch ist etwas wie O (1 / n ) völlig sinnvoll, wenn wir die mathematische Definition der Big O-Notation nehmen :
Jetzt können Sie 1 / x leicht durch g ( x ) ersetzen. Es ist offensichtlich, dass die obige Definition für einige f immer noch gilt .
Für die Schätzung des asymptotischen Laufzeitwachstums ist dies weniger praktikabel. Ein aussagekräftiger Algorithmus kann nicht schneller werden, wenn die Eingabe wächst. Natürlich können Sie einen beliebigen Algorithmus erstellen, um dies zu erfüllen, z. B. den folgenden:
def get_faster(list):
how_long = (1 / len(list)) * 100000
sleep(how_long)
Es ist klar, dass diese Funktion weniger Zeit benötigt, wenn die Eingabegröße zunimmt… zumindest bis zu einem von der Hardware erzwungenen Grenzwert (Genauigkeit der Zahlen, minimale sleep
Wartezeit, Zeit für die Verarbeitung von Argumenten usw.): Dieser Grenzwert wäre dann a konstante Untergrenze, so dass die obige Funktion immer noch Laufzeit O (1) hat.
Tatsächlich gibt es jedoch reale Algorithmen, bei denen die Laufzeit (zumindest teilweise) mit zunehmender Eingabegröße abnehmen kann. Beachten Sie jedoch, dass diese Algorithmen kein Laufzeitverhalten unterhalb von O (1) aufweisen. Trotzdem sind sie interessant. Nehmen Sie zum Beispiel den sehr einfachen Textsuchalgorithmus von Horspool . Hier nimmt die erwartete Laufzeit mit zunehmender Länge des Suchmusters ab (eine zunehmende Länge des Heuhaufens erhöht jedoch erneut die Laufzeit).
Ja.
Es gibt genau einen Algorithmus mit Laufzeit O (1 / n), den "leeren" Algorithmus.
Wenn ein Algorithmus O (1 / n) ist, bedeutet dies, dass er asymptotisch in weniger Schritten ausgeführt wird als der Algorithmus, der aus einem einzelnen Befehl besteht. Wenn es für alle n> n0 in weniger als einem Schritt ausgeführt wird, darf es für diese n überhaupt keine Anweisung enthalten. Da die Prüfung 'wenn n> n0' mindestens 1 Befehl kostet, darf sie für alle n keinen Befehl enthalten.
Fazit: Der einzige Algorithmus, der O (1 / n) ist, ist der leere Algorithmus, der aus keiner Anweisung besteht.
Scharfzahn ist richtig, O (1) ist die bestmögliche Leistung. Dies bedeutet jedoch keine schnelle Lösung, sondern nur eine zeitlich festgelegte Lösung.
Eine interessante Variante, und vielleicht wird wirklich vorgeschlagen, welche Probleme mit wachsender Bevölkerung leichter werden . Ich kann mir eine, wenn auch erfundene und ironische Antwort vorstellen:
Haben zwei Personen in einem Set denselben Geburtstag? Wenn n 365 überschreitet, geben Sie true zurück. Obwohl für weniger als 365, ist dies O (n ln n). Vielleicht keine gute Antwort, da das Problem nicht langsam einfacher wird, sondern nur zu O (1) für n> 365 wird.
Das ist doch nicht möglich. Die Definition von Big-O ist nicht größer als Ungleichung:
A(n) = O(B(n))
<=>
exists constants C and n0, C > 0, n0 > 0 such that
for all n > n0, A(n) <= C * B(n)
Das B (n) ist also tatsächlich der Maximalwert. Wenn es daher mit zunehmendem n abnimmt, ändert sich die Schätzung nicht.
Aus meiner vorherigen Erfahrung mit der Big-O-Notation geht hervor, dass O (1) ist, selbst wenn Sie einen Schritt benötigen (z. B. Überprüfen einer Variablen, Ausführen einer Zuweisung).
Beachten Sie, dass O (1) dasselbe wie O (6) ist, da die "Konstante" keine Rolle spielt. Deshalb sagen wir, dass O (n) dasselbe ist wie O (3n).
Wenn Sie also nur einen Schritt benötigen, ist das O (1) ... und da Ihr Programm mindestens einen Schritt benötigt, kann ein Algorithmus mindestens O (1) ausführen. Wenn wir es nicht tun, dann ist es O (0), denke ich? Wenn wir überhaupt etwas tun, dann ist es O (1), und das ist das Minimum, das es gehen kann.
(Wenn wir uns dafür entscheiden, es nicht zu tun, kann es zu einer Zen- oder Tao-Frage werden ... im Bereich der Programmierung ist O (1) immer noch das Minimum).
Oder wie wäre es damit:
Programmierer : Chef, ich habe einen Weg gefunden, dies in O (1) Zeit zu tun!
Chef : Keine Notwendigkeit, wir sind heute Morgen bankrott.
Programmierer : Oh, dann wird es O (0).
Nein das ist nicht möglich:
Da n in 1 / n gegen unendlich tendiert, erreichen wir schließlich 1 / (inf), was effektiv 0 ist.
Somit wäre die Big-Oh-Klasse des Problems O (0) mit einem massiven n, aber näher an der konstanten Zeit mit einem niedrigen n. Dies ist nicht sinnvoll, da das einzige, was schneller als in konstanter Zeit erledigt werden kann, Folgendes ist:
void nothing() {};
Und auch das ist fraglich!
Sobald Sie einen Befehl ausführen, befinden Sie sich in mindestens O (1). Nein, wir können keine Big-Oh-Klasse von O (1 / n) haben!
Was ist, wenn die Funktion überhaupt nicht ausgeführt wird (NOOP)? oder mit einem festen Wert. Zählt das?
Ich benutze oft O (1 / n), um Wahrscheinlichkeiten zu beschreiben, die kleiner werden, wenn die Eingaben größer werden - zum Beispiel ist die Wahrscheinlichkeit, dass eine faire Münze bei log2 (n) -Flips auftaucht, O (1 / n).
O (1) bedeutet einfach "konstante Zeit".
Wenn Sie einer Schleife [1] einen frühen Exit hinzufügen, verwandeln Sie (in Big-O-Notation) einen O (1) -Algorithmus in O (n), machen ihn aber schneller.
Der Trick ist im Allgemeinen, dass der Algorithmus mit konstanter Zeit der beste ist und linear besser als exponentiell ist, aber für kleine Mengen von n könnte der exponentielle Algorithmus tatsächlich schneller sein.
1: Für dieses Beispiel wird eine statische Listenlänge angenommen
Für alle, die diese Frage lesen und verstehen möchten, worum es in der Unterhaltung geht, könnte dies helfen:
| |constant |logarithmic |linear| N-log-N |quadratic| cubic | exponential |
| n | O(1) | O(log n) | O(n) |O(n log n)| O(n^2) | O(n^3) | O(2^n) |
| 1 | 1 | 1 | 1| 1| 1| 1 | 2 |
| 2 | 1 | 1 | 2| 2| 4| 8 | 4 |
| 4 | 1 | 2 | 4| 8| 16| 64 | 16 |
| 8 | 1 | 3 | 8| 24| 64| 512 | 256 |
| 16 | 1 | 4 | 16| 64| 256| 4,096 | 65536 |
| 32 | 1 | 5 | 32| 160| 1,024| 32,768 | 4,294,967,296 |
| 64 | 1 | 6 | 64| 384| 4,069| 262,144 | 1.8 x 10^19 |
Ich glaube, Quantenalgorithmen können mehrere Berechnungen "gleichzeitig" über Überlagerung durchführen ...
Ich bezweifle, dass dies eine nützliche Antwort ist.
Viele Menschen haben die richtige Antwort erhalten (Nein) Hier ist eine andere Möglichkeit, dies zu beweisen: Um eine Funktion zu haben, müssen Sie die Funktion aufrufen und eine Antwort zurückgeben. Dies dauert eine gewisse konstante Zeit. AUCH WENN der Rest der Verarbeitung für größere Eingaben weniger Zeit in Anspruch nahm, dauert das Ausdrucken der Antwort (von der wir annehmen können, dass es sich um ein einzelnes Bit handelt) mindestens konstant.
Wenn eine Lösung vorhanden ist, kann diese in konstanter Zeit = sofort vorbereitet und abgerufen werden. Verwenden Sie beispielsweise eine LIFO-Datenstruktur, wenn Sie wissen, dass die Sortierabfrage in umgekehrter Reihenfolge erfolgt. Dann sind die Daten bereits sortiert, vorausgesetzt, das entsprechende Modell (LIFO) wurde ausgewählt.
Welche Probleme werden mit wachsender Bevölkerung einfacher? Eine Antwort ist eine Sache wie Bittorrent, bei der die Download-Geschwindigkeit eine inverse Funktion der Anzahl der Knoten ist. Im Gegensatz zu einem Auto, das langsamer wird, je mehr Sie es laden, beschleunigt ein Filesharing-Netzwerk wie Bittorrent die Anzahl der verbundenen Knoten.
Sie können nicht unter O (1) gehen, jedoch ist O (k), wo k kleiner als N ist, möglich. Wir haben sie sublineare Zeitalgorithmen genannt . Bei einigen Problemen kann der sublineare Zeitalgorithmus nur ungefähre Lösungen für ein bestimmtes Problem liefern. Manchmal ist eine ungefähre Lösung jedoch in Ordnung, wahrscheinlich weil der Datensatz zu groß ist oder weil er viel zu rechenintensiv ist, um alle zu berechnen.
Was ist damit:
void FindRandomInList(list l)
{
while(1)
{
int rand = Random.next();
if (l.contains(rand))
return;
}
}
Mit zunehmender Größe der Liste nimmt die erwartete Laufzeit des Programms ab.
constains
ist O (1)
O (1 / n) ist nicht kleiner als O (1). Dies bedeutet im Grunde, dass je mehr Daten Sie haben, desto schneller geht der Algorithmus. Angenommen, Sie erhalten ein Array und füllen es immer mit bis zu 10 100 Elementen aus, wenn es weniger enthält, und tun nichts, wenn es mehr gibt. Dieser ist natürlich nicht O (1 / n), sondern so etwas wie O (-n) :) Schade, dass die O-Big-Notation keine negativen Werte zulässt.
Wie bereits erwähnt, kann es abgesehen von der möglichen Ausnahme der Nullfunktion keine geben O(1/n)
Funktionen geben, da sich die benötigte Zeit 0 nähern muss.
Natürlich gibt es einige Algorithmen, wie die von Konrad definierten, die weniger als O(1)
zumindest in gewissem Sinne sein sollten.
def get_faster(list):
how_long = 1/len(list)
sleep(how_long)
Wenn Sie diese Algorithmen untersuchen möchten, sollten Sie entweder Ihre eigene asymptotische Messung oder Ihren eigenen Zeitbegriff definieren. Zum Beispiel könnte ich in dem obigen Algorithmus die Verwendung einer Anzahl von "freien" Operationen eine festgelegte Anzahl von Malen erlauben. Wenn ich im obigen Algorithmus t 'definiere, indem ich die Zeit für alles außer dem Schlaf ausschließe, dann ist t' = 1 / n, was O (1 / n) ist. Es gibt wahrscheinlich bessere Beispiele, da das asymptotische Verhalten trivial ist. Tatsächlich bin ich mir sicher, dass jemand da draußen Sinne finden kann, die nicht triviale Ergebnisse liefern.
Die meisten anderen Antworten interpretieren Big-O so, dass es sich ausschließlich um die Laufzeit eines Algorithmus handelt. Da die Frage dies jedoch nicht erwähnte, hielt ich es für erwähnenswert, die andere Anwendung von Big-O in der numerischen Analyse zu erwähnen, bei der es um Fehler geht.
Viele Algorithmen können O (h ^ p) oder O (n ^ {- p}) sein, je nachdem, ob es sich um die Schrittgröße (h) oder die Anzahl der Unterteilungen (n) handelt. Zum Beispiel in Eulers Methode suchen Sie beispielsweise nach einer Schätzung von y (h), vorausgesetzt, Sie kennen y (0) und dy / dx (die Ableitung von y). Ihre Schätzung von y (h) ist genauer, je näher h an 0 liegt. Um also y (x) für ein beliebiges x zu finden, nimmt man das Intervall 0 bis x, teilt es bis zu n Teilen auf und führt die Euler-Methode aus an jedem Punkt, um von y (0) zu y (x / n) zu y (2x / n) zu gelangen, und so weiter.
Die Euler-Methode ist also ein O (h) - oder O (1 / n) -Algorithmus, bei dem h normalerweise als Schrittgröße und n als die Häufigkeit interpretiert wird, mit der Sie ein Intervall teilen.
Aufgrund von Gleitkomma-Rundungsfehlern können Sie in realen numerischen Analyseanwendungen auch O (1 / h) haben . Je kleiner Sie Ihr Intervall machen, desto mehr Stornierungen treten bei der Implementierung bestimmter Algorithmen auf, desto mehr signifikante Ziffern gehen verloren und daher mehr Fehler, die durch den Algorithmus weitergegeben werden.
Wenn Sie für die Euler-Methode Gleitkommazahlen verwenden, verwenden Sie einen ausreichend kleinen Schritt und eine Stornierung, und Sie fügen einer großen Zahl eine kleine Zahl hinzu, wobei die große Zahl unverändert bleibt. Für Algorithmen, die die Ableitung durch Subtrahieren von zwei Zahlen von einer Funktion berechnen, die an zwei sehr engen Positionen ausgewertet wird, wobei y '(x) mit (y (x + h) - y (x) / h) in glatten Funktionen y angenähert wird (x + h) nähert sich y (x), was zu einer großen Aufhebung und einer Schätzung für die Ableitung mit weniger signifikanten Zahlen führt. Dies wird sich wiederum auf jeden Algorithmus übertragen, für den Sie die Ableitung benötigen (z. B. ein Randwertproblem).
OK, ich habe ein bisschen darüber nachgedacht, und vielleicht gibt es einen Algorithmus, der dieser allgemeinen Form folgen könnte:
Sie müssen das Problem des Handlungsreisenden für ein Diagramm mit 1000 Knoten berechnen. Sie erhalten jedoch auch eine Liste der Knoten, die Sie nicht besuchen können. Je größer die Liste der nicht sichtbaren Knoten wird, desto leichter lässt sich das Problem lösen.
Ich sehe einen Algorithmus, der zugegebenermaßen O (1 / n) zu einer Obergrenze gehört:
Sie haben eine große Reihe von Eingaben, die sich aufgrund von etwas außerhalb der Routine ändern (möglicherweise spiegeln sie die Hardware wider oder es könnte sogar ein anderer Kern im Prozessor sein, der dies tut), und Sie müssen einen zufälligen, aber gültigen auswählen.
Wenn es sich nicht ändern würde, würden Sie einfach eine Liste von Elementen erstellen, eine zufällig auswählen und O (1) Zeit erhalten. Die Dynamik der Daten schließt jedoch das Erstellen einer Liste aus. Sie müssen lediglich zufällig prüfen und die Gültigkeit der Prüfung testen. (Und beachten Sie, dass es von Natur aus keine Garantie gibt, dass die Antwort bei der Rückgabe noch gültig ist. Dies könnte immer noch Verwendung haben - beispielsweise die KI für eine Einheit in einem Spiel. Sie könnte auf ein Ziel schießen, das währenddessen außer Sichtweite war den Abzug betätigen.)
Dies hat eine Worst-Case-Leistung von unendlich, aber eine durchschnittliche Fallleistung, die abnimmt, wenn sich der Datenraum füllt.
Bei der numerischen Analyse sollten Approximationsalgorithmen eine subkonstante asymptotische Komplexität in der Approximationstoleranz aufweisen.
class Function
{
public double[] ApproximateSolution(double tolerance)
{
// if this isn't sub-constant on the parameter, it's rather useless
}
}
Ich denke weniger als O (1) ist nicht möglich. Jede von algo benötigte Zeit wird als O (1) bezeichnet. Aber für O (1 / n) wie wäre es mit der folgenden Funktion. (Ich weiß, dass es in dieser Lösung bereits viele Varianten gibt, aber ich denke, sie haben alle einige Mängel (nicht schwerwiegend, sie erklären das Konzept gut). Hier also eine, nur aus Gründen der Argumentation:
def 1_by_n(n, C = 10): #n could be float. C could be any positive number
if n <= 0.0: #If input is actually 0, infinite loop.
while True:
sleep(1) #or pass
return #This line is not needed and is unreachable
delta = 0.0001
itr = delta
while delta < C/n:
itr += delta
Mit zunehmendem n nimmt die Funktion daher immer weniger Zeit in Anspruch. Es wird auch sichergestellt, dass die Funktion für immer zurückkehrt, wenn die Eingabe tatsächlich 0 ist.
Man könnte argumentieren, dass es durch die Präzision der Maschine begrenzt wird. Somit hat es eine Obergrenze, es ist O (1). Aber wir können das auch umgehen, indem wir Eingaben von n und C in Zeichenfolgen vornehmen. Das Hinzufügen und Vergleichen erfolgt über eine Zeichenfolge. Die Idee ist, dass wir damit n beliebig klein reduzieren können. Somit ist die Obergrenze der Funktion nicht begrenzt, selbst wenn wir n = 0 ignorieren.
Ich glaube auch, dass wir nicht einfach sagen können, dass die Laufzeit O (1 / n) ist. Aber wir sollten so etwas wie O sagen (1 + 1 / n)
Es kann möglich sein, einen Algorithmus zu konstruieren, der O (1 / n) ist. Ein Beispiel wäre eine Schleife, die ein Vielfaches von f (n) -n Mal wiederholt, wobei f (n) eine Funktion ist, deren Wert garantiert größer als n ist und die Grenze von f (n) -n, wenn n gegen unendlich geht, ist Null. Die Berechnung von f (n) müsste auch für alle n konstant sein. Ich weiß nicht ohne weiteres, wie f (n) aussehen würde oder welche Anwendung ein solcher Algorithmus haben würde. Meiner Meinung nach könnte jedoch eine solche Funktion existieren, aber der resultierende Algorithmus hätte keinen anderen Zweck, als die Möglichkeit eines Algorithmus mit zu beweisen O (1 / n).
Ich weiß nichts über Algorithmen, aber in zufälligen Algorithmen treten Komplexitäten von weniger als O (1) auf. Tatsächlich ist o (1) (wenig o) kleiner als O (1). Diese Art von Komplexität tritt normalerweise in randomisierten Algorithmen auf. Zum Beispiel, wie Sie sagten, wenn die Wahrscheinlichkeit eines Ereignisses in der Größenordnung von 1 / n liegt, bezeichnen sie es mit o (1). Oder wenn sie sagen wollen, dass etwas mit hoher Wahrscheinlichkeit passiert (z. B. 1 - 1 / n), bezeichnen sie es mit 1 - o (1).
Wenn die Antwort unabhängig von den Eingabedaten gleich ist, haben Sie einen O (0) -Algorithmus.
oder mit anderen Worten - die Antwort ist bekannt, bevor die Eingabedaten gesendet werden - die Funktion könnte optimiert werden - also O (0)
Die Big-O-Notation stellt das Worst-Case-Szenario für einen Algorithmus dar, der nicht mit seiner typischen Laufzeit identisch ist. Es ist einfach zu beweisen, dass ein O (1 / n) -Algorithmus ein O (1) -Algorithmus ist. Per Definition ist
O (1 / n) -> T (n) <= 1 / n für alle n> = C> 0
O (1 / n) -> T (n) <= 1 / C, seit 1 / n <= 1 / C für alle n> = C
O (1 / n) -> O (1), da die Big-O-Notation Konstanten ignoriert (dh der Wert von C spielt keine Rolle)
hashtable-contains
Algorithmus, die als O (1) bezeichnet werden kann - und der schlechteste Fall kann sehr genau als Theta (n) angegeben werden! Omega und Theta können einfach verwendet werden, um andere Grenzen zu bezeichnen, aber um es noch einmal zu sagen : Sie haben nichts mit dem Durchschnitt oder dem besten Fall zu tun.
Nichts ist kleiner als O (1) Die Big-O-Notation impliziert die größte Komplexitätsordnung für einen Algorithmus
Wenn ein Algorithmus eine Laufzeit von n ^ 3 + n ^ 2 + n + 5 hat, dann ist es O (n ^ 3). Die niedrigeren Potenzen spielen hier überhaupt keine Rolle, da n ^> als n -> Inf im Vergleich zu irrelevant ist n ^ 3
Ebenso wie n -> Inf ist O (1 / n) im Vergleich zu O (1) irrelevant, daher ist 3 + O (1 / n) dasselbe wie O (1), wodurch O (1) die kleinstmögliche Berechnung ist Komplexität
inline void O0Algorithm() {}
Hier ist ein einfacher O (1 / n) -Algorithmus. Und es macht sogar etwas Interessantes!
function foo(list input) {
int m;
double output;
m = (1/ input.size) * max_value;
output = 0;
for (int i = 0; i < m; i++)
output+= random(0,1);
return output;
}
O (1 / n) ist möglich, da es beschreibt, wie sich die Ausgabe einer Funktion mit zunehmender Größe der Eingabe ändert. Wenn wir die Funktion 1 / n verwenden, um die Anzahl der Befehle zu beschreiben, die eine Funktion ausführt, ist es nicht erforderlich, dass die Funktion für jede Eingabegröße Nullbefehle akzeptiert. Es ist vielmehr so, dass für jede Eingabegröße n oberhalb eines Schwellenwerts die Anzahl der erforderlichen Befehle oben durch eine positive Konstante multipliziert mit 1 / n begrenzt ist. Da es keine tatsächliche Zahl gibt, für die 1 / n 0 ist, und die Konstante positiv ist, gibt es keinen Grund, warum die Funktion gezwungen wäre, 0 oder weniger Anweisungen zu akzeptieren.