Esistono algoritmi O (1 / n)?
O qualcos'altro che è inferiore a O (1)?
Esistono algoritmi O (1 / n)?
O qualcos'altro che è inferiore a O (1)?
Risposte:
Questa domanda non è così stupida come potrebbe sembrare. Almeno teoricamente, qualcosa come O (1 / n ) è completamente sensato quando prendiamo la definizione matematica della notazione Big O :
Ora puoi facilmente sostituire g ( x ) con 1 / x ... è ovvio che la definizione sopra vale ancora per qualche f .
Ai fini della stima della crescita runtime asintotica, questo è meno praticabile ... un algoritmo significativo non può essere più veloce man mano che l'input cresce. Certo, puoi costruire un algoritmo arbitrario per soddisfare questo, ad esempio il seguente:
def get_faster(list):
how_long = (1 / len(list)) * 100000
sleep(how_long)
Chiaramente, questa funzione impiega meno tempo man mano che le dimensioni dell'input aumentano ... almeno fino a un certo limite, applicato dall'hardware (precisione dei numeri, tempo minimo che sleep
può attendere, tempo per elaborare argomenti ecc.): Questo limite sarebbe quindi un limite inferiore costante quindi la funzione sopra ha ancora il runtime O (1).
Ma ci sono in realtà algoritmi del mondo reale in cui il tempo di esecuzione può diminuire (almeno parzialmente) quando aumenta la dimensione dell'input. Tuttavia, questi algoritmi non mostreranno un comportamento di runtime inferiore a O (1). Sono comunque interessanti. Ad esempio, prendi il semplicissimo algoritmo di ricerca del testo di Horspool . Qui, il tempo di esecuzione previsto diminuirà con l'aumentare della lunghezza del modello di ricerca (ma l'aumento della lunghezza del pagliaio aumenterà ancora una volta il tempo di esecuzione).
Sì.
Esiste esattamente un algoritmo con runtime O (1 / n), l'algoritmo "vuoto".
Per un algoritmo essere O (1 / n) significa che esegue asintoticamente in meno passaggi dell'algoritmo costituito da una singola istruzione. Se viene eseguito in meno passaggi di un passaggio per tutti n> n0, deve consistere esattamente in nessuna istruzione per quelli n. Dal momento che il controllo di 'if n> n0' costa almeno 1 istruzione, non deve consistere in istruzioni per tutte le n.
Riassumendo: l'unico algoritmo che è O (1 / n) è l'algoritmo vuoto, costituito da nessuna istruzione.
sharptooth è corretto, O (1) è la migliore prestazione possibile. Tuttavia, non implica una soluzione rapida, ma solo una soluzione a tempo fisso.
Una variante interessante, e forse ciò che viene realmente suggerito, è quali problemi diventano più facili man mano che la popolazione cresce. Mi viene in mente 1, anche se forzato e la risposta ironica:
Due persone in un set hanno lo stesso compleanno? Quando n supera 365, restituisce true. Sebbene per meno di 365, questo è O (n ln n). Forse non è un'ottima risposta poiché il problema non diventa lentamente più semplice ma diventa semplicemente O (1) per n> 365.
Non e possibile. La definizione di Big-O non è maggiore della disuguaglianza:
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)
Quindi B (n) è in realtà il valore massimo, quindi se diminuisce all'aumentare di n la stima non cambierà.
Dal mio precedente apprendimento della notazione O grande, anche se hai bisogno di 1 passo (come controllare una variabile, fare un compito), questo è O (1).
Nota che O (1) è uguale a O (6), perché la "costante" non ha importanza. Ecco perché diciamo che O (n) è uguale a O (3n).
Quindi se hai bisogno anche di 1 passaggio, questo è O (1) ... e poiché il tuo programma richiede almeno 1 passaggio, il minimo che un algoritmo può percorrere è O (1). A meno che non lo facciamo, allora è O (0), penso? Se facciamo qualsiasi cosa, allora è O (1), ed è il minimo che può andare.
(Se scegliamo di non farlo, allora potrebbe diventare una domanda Zen o Tao ... nel regno della programmazione, O (1) è ancora il minimo).
O che ne dici di questo:
programmatore : capo, ho trovato il modo di farlo in O (1) volta!
capo : non c'è bisogno di farlo, stamattina siamo in bancarotta.
programmatore : oh, allora diventa O (0).
No, non è possibile:
Dato che n tende all'infinito in 1 / n alla fine otteniamo 1 / (inf), che è effettivamente 0.
Pertanto, la classe big-oh del problema sarebbe O (0) con un n massiccio, ma più vicino al tempo costante con un n basso. Questo non è sensato, in quanto l'unica cosa che può essere fatta in tempi più rapidi di quelli costanti è:
void nothing() {};
E anche questo è discutibile!
Non appena si esegue un comando, ci si trova almeno in O (1), quindi no, non possiamo avere una classe big-oh di O (1 / n)!
Che dire di non eseguire affatto la funzione (NOOP)? o usando un valore fisso. Questo conta?
Uso spesso O (1 / n) per descrivere le probabilità che si riducono man mano che gli input diventano più grandi - ad esempio, la probabilità che una moneta giusta raggiunga la coda su log2 (n) flip è O (1 / n).
O (1) significa semplicemente "tempo costante".
Quando aggiungi un'uscita anticipata a un loop [1] stai (in notazione big-O) trasformando un algoritmo O (1) in O (n), ma rendendolo più veloce.
Il trucco è in generale che l'algoritmo a tempo costante è il migliore e lineare è migliore quindi esponenziale, ma per piccole quantità di n, l'algoritmo esponenziale potrebbe effettivamente essere più veloce.
1: supponendo una lunghezza di elenco statica per questo esempio
Per chiunque abbia letto questa domanda e voglia capire di cosa tratta la conversazione, questo potrebbe aiutare:
| |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 |
Credo che gli algoritmi quantistici possano eseguire più calcoli "contemporaneamente" tramite sovrapposizione ...
Dubito che questa sia una risposta utile.
molte persone hanno avuto la risposta corretta (No) Ecco un altro modo per dimostrarlo: per avere una funzione, devi chiamare la funzione e devi restituire una risposta. Questo richiede un certo periodo di tempo costante. ANCHE SE il resto dell'elaborazione ha richiesto meno tempo per input più grandi, stampare la risposta (che è possibile supporre sia un singolo bit) richiede almeno un tempo costante.
Se esiste una soluzione, è possibile prepararla e accedervi a tempo costante = immediatamente. Ad esempio, utilizzando una struttura di dati LIFO se si conosce che la query di ordinamento è per l'ordine inverso. Quindi i dati sono già ordinati, dato che è stato scelto il modello appropriato (LIFO).
Quali problemi diventano più facili con l'aumentare della popolazione? Una risposta è qualcosa di simile a bittorrent in cui la velocità di download è una funzione inversa del numero di nodi. Contrariamente a un'auto, che rallenta quanto più la carichi, una rete di condivisione file come bittorrent accelera il maggior numero di nodi connessi.
Non puoi andare sotto O (1), tuttavia O (k) dove k è minore di N è possibile. Li abbiamo chiamati algoritmi di tempo sublineare . In alcuni problemi, l'algoritmo del tempo sublineare può fornire solo soluzioni approssimative a un problema specifico. Tuttavia, a volte, una soluzione approssimativa va bene, probabilmente perché il set di dati è troppo grande o che è troppo costoso dal punto di vista computazionale per calcolare tutto.
Che dire di questo:
void FindRandomInList(list l)
{
while(1)
{
int rand = Random.next();
if (l.contains(rand))
return;
}
}
con l'aumentare della dimensione dell'elenco, il tempo di esecuzione previsto del programma diminuisce.
constains
è O (1)
O (1 / n) non è inferiore a O (1), in pratica significa che più dati hai, più l'algoritmo procede. Supponi di avere un array e di riempirlo sempre fino a 10 100 elementi se ha meno di quello e non fare nulla se c'è di più. Questo non è O (1 / n) ovviamente ma qualcosa come O (-n) :) Pessima notazione O-big non consente valori negativi.
Come è stato sottolineato, a parte la possibile eccezione della funzione nulla, non ci possono essere O(1/n)
funzioni, poiché il tempo impiegato dovrà avvicinarsi a 0.
Certo, ci sono alcuni algoritmi, come quello definito da Konrad, che sembrano essere meno che O(1)
in un certo senso.
def get_faster(list):
how_long = 1/len(list)
sleep(how_long)
Se si desidera indagare su questi algoritmi, è necessario definire la propria misurazione asintotica o la propria nozione di tempo. Ad esempio, nel suddetto algoritmo, potrei consentire l'uso di un numero di operazioni "libere" per un determinato numero di volte. Nell'algoritmo sopra, se definisco t 'escludendo il tempo per tutto tranne che per il sonno, allora t' = 1 / n, che è O (1 / n). Probabilmente ci sono esempi migliori, poiché il comportamento asintotico è banale. In effetti, sono sicuro che qualcuno là fuori può trovare sensi che danno risultati non banali.
La maggior parte del resto delle risposte interpreta il big-O come esclusivamente relativo al tempo di esecuzione di un algoritmo. Ma dal momento che la domanda non l'ha menzionata, ho pensato che valesse la pena menzionare l'altra applicazione del big-O nell'analisi numerica, che riguarda l'errore.
Molti algoritmi possono essere O (h ^ p) o O (n ^ {- p}) a seconda che si parli di dimensioni del gradino (h) o numero di divisioni (n). Ad esempio, nel metodo di Eulero , cerchi una stima di y (h) dato che conosci y (0) e dy / dx (la derivata di y). La tua stima di y (h) è più accurata quanto più h è vicino a 0. Quindi per trovare y (x) per qualche x arbitraria, si prende l'intervallo da 0 a x, lo divide fino a n pezzi ed esegue il metodo di Eulero in ogni punto, per andare da y (0) a y (x / n) a y (2x / n) e così via.
Quindi il metodo di Eulero è quindi un algoritmo O (h) o O (1 / n), in cui h viene generalmente interpretato come una dimensione del gradino e n viene interpretato come il numero di volte in cui dividi un intervallo.
È inoltre possibile avere O (1 / h) in applicazioni di analisi numerica reali, a causa di errori di arrotondamento in virgola mobile . Minore è l'intervallo, maggiore è la cancellazione per l'implementazione di determinati algoritmi, maggiore perdita di cifre significative e quindi più errore, che viene propagato attraverso l'algoritmo.
Per il metodo di Eulero, se stai usando punti mobili, usa un passo e una cancellazione abbastanza piccoli e stai aggiungendo un piccolo numero a un numero grande, lasciando invariato il numero grande. Per algoritmi che calcolano la derivata sottraendo l'uno dall'altro due numeri da una funzione valutata in due posizioni molto vicine, approssimando y '(x) con (y (x + h) - y (x) / h), nelle funzioni regolari y (x + h) si avvicina a y (x) con conseguente grande annullamento e una stima per il derivato con meno cifre significative. Questo a sua volta si propaga a qualunque algoritmo per cui si richiede la derivata (ad esempio, un problema con il valore limite).
OK, ci ho pensato un po ', e forse esiste un algoritmo che potrebbe seguire questa forma generale:
Devi calcolare il problema del commesso viaggiatore per un grafico a 1000 nodi, tuttavia ti viene anche fornito un elenco di nodi che non puoi visitare. Man mano che l'elenco dei nodi non visibili si allarga, il problema diventa più facile da risolvere.
Vedo un algoritmo che è O (1 / n) ammesso per un limite superiore:
Hai una grande serie di input che cambiano a causa di qualcosa di esterno alla routine (forse riflettono l'hardware o potrebbe anche essere un altro core nel processore che lo sta facendo.) E devi selezionarne uno casuale ma valido.
Ora, se non stesse cambiando, faresti semplicemente un elenco di elementi, scegline uno a caso e otterrai O (1) tempo. Tuttavia, la natura dinamica dei dati preclude la creazione di un elenco, è sufficiente sondare casualmente e testare la validità del probe. (E nota che intrinsecamente non esiste alcuna garanzia che la risposta sia ancora valida quando viene restituita. Questo potrebbe avere ancora degli usi - diciamo, l'intelligenza artificiale per un'unità in un gioco. Potrebbe sparare a un bersaglio che è scomparso mentre era premendo il grilletto.)
Ciò ha una prestazione nel caso peggiore dell'infinito, ma una prestazione nel caso medio che diminuisce quando lo spazio dati si riempie.
Nell'analisi numerica, gli algoritmi di approssimazione dovrebbero avere una complessità asintotica sub-costante nella tolleranza di approssimazione.
class Function
{
public double[] ApproximateSolution(double tolerance)
{
// if this isn't sub-constant on the parameter, it's rather useless
}
}
Immagino che meno di O (1) non sia possibile. Ogni tempo impiegato da algo è definito come O (1). Ma per O (1 / n) che ne dici della funzione qui sotto. (So che ci sono già molte varianti presentate in questa soluzione, ma immagino che abbiano tutti dei difetti (non importanti, spiegano bene il concetto). Quindi eccone una, solo per ragioni di argomento:
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
Pertanto, all'aumentare di n, la funzione richiederà sempre meno tempo. Inoltre è garantito che se l'ingresso è effettivamente 0, allora la funzione impiegherà un'eternità a tornare.
Si potrebbe sostenere che sarà limitato dalla precisione della macchina. quindi sinc eit ha un limite superiore è O (1). Ma possiamo anche bypassarlo, prendendo input di n e C in stringa. E l'aggiunta e il confronto vengono eseguiti su stringa. L'idea è che, con questo, possiamo ridurre n in modo arbitrario. Pertanto il limite superiore della funzione non è limitato, anche quando ignoriamo n = 0.
Credo anche che non possiamo semplicemente dire che il tempo di esecuzione è O (1 / n). Ma dovremmo dire qualcosa come O (1 + 1 / n)
Potrebbe essere possibile costruire un algoritmo O (1 / n). Un esempio potrebbe essere un ciclo che itera un multiplo di f (n) -n volte in cui f (n) è una funzione il cui valore è garantito essere maggiore di n e il limite di f (n) -n quando n si avvicina all'infinito è zero. Anche il calcolo di f (n) dovrebbe essere costante per tutti n. Non so a mano come sarebbe f (n) o quale applicazione avrebbe un tale algoritmo, a mio avviso tuttavia tale funzione potrebbe esistere ma l'algoritmo risultante non avrebbe altro scopo che dimostrare la possibilità di un algoritmo con O (1 / n).
Non conosco algoritmi ma le complessità inferiori a O (1) appaiono in algoritmi randomizzati. In realtà, o (1) (piccola o) è inferiore a O (1). Questo tipo di complessità appare di solito negli algoritmi randomizzati. Ad esempio, come hai detto, quando la probabilità di un evento è dell'ordine di 1 / n, lo denotano con o (1). O quando vogliono dire che qualcosa accade con alta probabilità (es. 1 - 1 / n) lo denotano con 1 - o (1).
Se la risposta è la stessa indipendentemente dai dati di input, hai un algoritmo O (0).
o in altre parole - la risposta è nota prima che vengano inviati i dati di input - la funzione potrebbe essere ottimizzata - quindi O (0)
La notazione Big-O rappresenta lo scenario peggiore per un algoritmo che non è la stessa cosa del suo tipico tempo di esecuzione. È semplice dimostrare che un algoritmo O (1 / n) è un algoritmo O (1). Per definizione,
O (1 / n) -> T (n) <= 1 / n, per tutti n> = C> 0
O (1 / n) -> T (n) <= 1 / C, poiché 1 / n <= 1 / C per tutti n> = C
O (1 / n) -> O (1), poiché la notazione Big-O ignora le costanti (ovvero il valore di C non ha importanza)
hashtable-contains
dell'algoritmo che può essere indicato come O (1) - e il caso peggiore può essere dato in modo molto preciso come Theta (n)! Omega e Theta possono semplicemente essere usati per indicare altri limiti, ma per dirlo di nuovo : non hanno nulla a che fare con il caso medio o migliore.
Nulla è più piccolo di O (1) La notazione Big-O implica il più grande ordine di complessità per un algoritmo
Se un algoritmo ha un tempo di esecuzione di n ^ 3 + n ^ 2 + n + 5, allora è O (n ^ 3) I poteri inferiori non contano affatto qui perché come n -> Inf, n ^ 2 sarà irrilevante rispetto a n ^ 3
Allo stesso modo come n -> Inf, O (1 / n) sarà irrilevante rispetto a O (1), quindi 3 + O (1 / n) sarà lo stesso di O (1) rendendo così O (1) il più piccolo possibile computazionale complessità
inline void O0Algorithm() {}
Ecco un semplice algoritmo O (1 / n). E fa anche qualcosa di interessante!
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) è possibile in quanto descrive come cambia l'output di una funzione data la dimensione crescente dell'input. Se stiamo usando la funzione 1 / n per descrivere il numero di istruzioni eseguite da una funzione, non è necessario che la funzione prenda zero istruzioni per qualsiasi dimensione di input. Piuttosto, è che per ogni dimensione di input, n al di sopra di una certa soglia, il numero di istruzioni richieste è limitato da una costante positiva moltiplicata per 1 / n. Poiché non esiste un numero effettivo per il quale 1 / n è 0 e la costante è positiva, non vi è alcun motivo per cui la funzione sia vincolata a prendere 0 o meno istruzioni.