Esistono algoritmi del mondo reale che superano notevolmente le prestazioni nella classe sottostante? [chiuso]


39

Ieri sera stavo discutendo con un altro programmatore che anche se qualcosa potrebbe essere O (1), un'operazione che è O (n) potrebbe sovraperformarla se c'è una grande costante nell'algoritmo O (1). Non era d'accordo, quindi l'ho portato qui.

Ci sono esempi di algoritmi che superano notevolmente quelli della classe sottostante? Ad esempio, O (n) è più veloce di O (1) o O (n 2 ) è più veloce di O (n).

Matematicamente questo può essere dimostrato per una funzione con limiti superiori asintotici, quando si ignorano i fattori costanti, ma esistono tali algoritmi in natura? E dove troverei esempi di questi? Per quali tipi di situazioni vengono utilizzati?


15
Anche per gli algoritmi "grandi", i più piccoli non sono necessariamente migliori. Ad esempio, l'eliminazione gaussiana è O (n ^ 3), ma ci sono algoritmi che possono farlo in O (n ^ 2), ma il coefficiente per l'algo temporale quadratico è così grande che le persone vanno semplicemente con O (n ^ 3) uno.
BlackJack,

11
Devi aggiungere "... per problemi del mondo reale" o qualcosa del genere per renderlo una domanda sensata. Altrimenti devi solo fare nabbastanza grande da compensare la costante (che è il punto della notazione big-O).
Starblue,

8
Non prendere la notazione big-O per la velocità.
Codismo

16
Il punto della notazione big-O non è dirti quanto velocemente corre un algoritmo, ma quanto bene si ridimensiona.
BlueRaja - Danny Pflughoeft l'

4
Sono sorpreso che nessuno abbia menzionato l'algoritmo Simplex per la risoluzione di LP. Ha un caso peggiore esponenziale con un tempo di esecuzione previsto lineare. In pratica, è abbastanza veloce. È banale costruire un problema che presenti anche il runtime nel caso peggiore. Inoltre, è molto usato.
ccoakley,

Risposte:


45

Ricerche in tabelle di dati fisse molto piccole. Una tabella hash ottimizzata può essere O (1) e tuttavia più lenta di una ricerca binaria o persino di una ricerca lineare a causa del costo del calcolo dell'hash.


14
Più precisamente, la ricerca hashtable è O (m) dove m è la dimensione della chiave. Puoi chiamare quel O (1) solo se la dimensione della chiave è costante. Inoltre, di solito è ammortizzato, altrimenti la tabella non può crescere / restringersi. Gli alberi ternari possono spesso battere le tabelle hash per le ricerche di stringhe in contesti in cui le stringhe non vengono trovate abbastanza spesso - la ricerca dell'albero ternario spesso scoprirà che la chiave non è presente mentre controlla ancora il primo carattere o due della stringa, dove il la versione di hashtable non ha ancora calcolato l'hash.
Steve314,

2
Adoro la risposta di Loren Pechtel e il primo commento di Steve314. In realtà l'ho visto accadere. Se si crea una classe Java che ha un metodo hashcode () che impiega troppo tempo per restituire il valore hash (e non può / non può memorizzarlo nella cache), utilizzare le istanze di tale classe in una raccolta di tipo hash (come HashSet) renderà la raccolta MOLTO più lenta di una raccolta di tipo array (come ArrayList).
Drago Shivan,

1
@ Steve314: perché pensi che le funzioni hash siano O (m) dove m è la dimensione della chiave? Le funzioni di hash possono essere O (1) anche se hai a che fare con stringhe (o altri tipi complessi). Non c'è troppo valore per metterlo nella definizione formale che la semplice realizzazione della funzione hash potrebbe cambiare significativamente la complessità se si sceglie una struttura dati errata (tabella hash) per l'input (la dimensione della chiave è imprevedibile).
Codismo

1
@ Steve314: Nota che ho detto tabelle di dati fissi. Non crescono. Inoltre, si ottengono prestazioni O (1) da una tabella hash solo se è possibile ottimizzare la chiave per assicurarsi che non vi siano collisioni.
Loren Pechtel,

1
@Loren - rigorosamente, se la tabella ha una dimensione fissa, c'è un tempo massimo costante che puoi dedicare alla ricerca di uno spazio libero. Cioè, al massimo, puoi aver bisogno di controllare n-1 slot già riempiti dove n è la dimensione costante della tabella. Quindi una tabella hash di dimensioni fisse è davvero O (1), senza bisogno di analisi ammortizzate. Questo non significa che non ti preoccupi che gli accessi diventino più lenti man mano che la tabella si riempie - solo che non è ciò che esprime O grande.
Steve314,

25

Moltiplicazione di matrici. L'algoritmo O naïve (n ^ 3) è spesso usato in pratica più veloce di O (n ^ 2.8) di Strassen per le matrici di piccole dimensioni; e Strassen è usato al posto dell'algoritmo O (n ^ 2.3) Coppersmith – Winograd per matrici più grandi.



2
Coppersmith-Winograd non viene MAI utilizzato. L'implementazione sarebbe di per sé un compito orribile e la costante è così grave che sarebbe irrealizzabile anche per i moderni problemi di matrice scientifica.
tskuzzy,

24

Un semplice esempio è la differenza tra vari algoritmi di ordinamento. Mergesort, Heapsort e alcuni altri sono O (n log n) . Quicksort è il caso peggiore O (n ^ 2) . Ma spesso Quicksort è più veloce, e in effetti si comporta in media come O (n log n) . Maggiori informazioni .

Un altro esempio è la generazione di un singolo numero di Fibonacci. L'algoritmo iterativo è O (n) , mentre l'algoritmo basato su matrice è O (log n) . Tuttavia, per la prima coppia di migliaia di numeri di Fibonacci, l'algoritmo iterativo è probabilmente più veloce. Questo dipende anche dall'implementazione ovviamente!

Gli algoritmi con una migliore prestazione asintotica possono contenere operazioni costose che non sono necessarie con un algoritmo con prestazioni peggiori ma operazioni più semplici. Alla fine, la notazione O ci dice qualcosa sulle prestazioni solo quando l'argomento su cui opera aumenta drammaticamente (si avvicina all'infinito).


Questa è una grande spiegazione di Big-O, ma non riesce ad affrontare la questione della domanda, che è per casi specifici in cui un algoritmo O (n) sarà più veloce di un O (1).
KyleWpppd,

Il numero uno di Fibonacci è leggermente spento. La dimensione dell'output è esponenziale nella dimensione dell'input, quindi è una differenza tra O (lg n * e ^ n) vs O (lg lg n * e ^ n).
Peter Taylor,

Addendum: nella migliore delle ipotesi. L'algoritmo basato su matrice si moltiplica con i numeri dell'ordine di 1,5 ^ n, quindi O (lg lg n * ne ^ n) potrebbe essere il miglior limite dimostrabile.
Peter Taylor,

1
Quicksort è normalmente descritto come O (n log n) prestazioni attese comunque - il caso peggiore è abbastanza improbabile per input casuali, e costruire una casualità in una prepass o nella selezione pivot significa che il caso peggiore nel complesso è molto improbabile per dimensioni di input significative. Il caso peggiore è meno rilevante del fatto che quicksort è (1) molto semplice e (2) molto intuitivo per la cache, entrambi i quali portano a fattori costanti significativamente migliori rispetto a molti altri algoritmi di ordinamento.
Steve314,

(2) è esattamente il tipo di considerazione esterna che deve essere presa in considerazione quando si guarda alle prestazioni di big-O. Algoritmicamente, Mergesort dovrebbe sempre sovraperformare Quicksort, ma l'utilizzo delle risorse e la localizzazione della cache generalmente invertono le loro posizioni di performance nel mondo reale.
Dan Lyons,

18

Nota: si prega di leggere i commenti di @ back2dos di seguito e di altri guru, in quanto sono in effetti più utili di quello che ho scritto - Grazie per tutti i collaboratori.

Penso dalla tabella qui sotto (presa da: notazione Big O , cerca "The Pessimistic Nature of Algorithms:"), puoi vedere che O (log n) non è sempre meglio di dire, O (n). Quindi, immagino che il tuo argomento sia valido.

Pic-1


6
La domanda voleva specifici esempi di algoritmi nel mondo reale. Questo non ha nessuno così com'è.
Megan Walker,

19
Non puoi vedere nulla su quel grafico, che risponderebbe alla domanda. È fuorviante. Questo grafico limita traccia le funzioni y = 1, y = log xe così via e l'intersezione di y = 1e y = xè in realtà il punto (1,1). Se questo fosse davvero corretto, di quanto ti direbbe, che gli algoritmi di maggiore complessità possono essere più veloci per 0 o 2 voci, il che è qualcosa di cui la gente difficilmente si preoccuperebbe. Ciò che il grafico non riesce completamente a prendere in considerazione (e da dove proviene la differenza di prestazione percepibile in questione) sono fattori costanti.
back2dos,

@ Samuel Walker, grazie per il commento. Il link fornito (Link-1) ha alcuni esempi di algoritmi per categoria.
NoChance,

5
@ back2dos: il grafico da solo non risponde alla domanda, ma può essere utilizzato per rispondere. La forma di ciascuna funzione visualizzata è la stessa per qualsiasi scala e fattore costante. Con questo il grafico mostra che, data la combinazione di funzioni, esiste un intervallo di input per i quali uno è più piccolo e un intervallo di input per cui l'altro è.
Jan Hudec,

2
@dan_waterworth, hai ragione, concederò quel punto e rimuoverò quel commento. Tuttavia, la risposta è errata o fuorviante sotto due aspetti: 1) Il punto centrale di Big-O è che dà un limite superiore alla complessità; è significativo solo per n grande perché eliminiamo esplicitamente termini più piccoli che vengono sopraffatti dal termine più grande man mano che n cresce. 2) Il punto della domanda è trovare esempi di due algoritmi in cui quello con il limite Big-O superiore supera quello con un limite inferiore. Questa risposta fallisce perché non fornisce tali esempi.
Caleb,

11

Per valori pratici di n, sì. Questo emerge molto nella teoria CS. Spesso esiste un algoritmo complicato che ha prestazioni big-Oh tecnicamente migliori, ma i fattori costanti sono così grandi da renderlo impraticabile.

Una volta ho avuto il mio professore di geometria computazionale che descriveva un algoritmo per triangolare un poligono in tempo lineare, ma ha finito con "molto complicato. Non credo che nessuno l'abbia effettivamente implementato" (!!).

Inoltre, i mucchi di fibonacci hanno caratteristiche migliori rispetto ai mucchi normali, ma non sono molto popolari perché in pratica non si comportano bene come i mucchi normali. Questo può arrivare ad altri algoritmi che usano i cumuli - per esempio, i percorsi più brevi di Dijkstra sono matematicamente più veloci con un mucchio di fibonacci, ma di solito non in pratica.


È più veloce per enormi grafici dell'ordine di circa 100.000 vertici.
tskuzzy,

I mucchi di Fibonacci furono anche il mio primo (in realtà, secondo) pensiero.
Konrad Rudolph,

10

Confronta l'inserimento in un elenco collegato e l'inserimento in un array ridimensionabile.

La quantità di dati deve essere abbastanza grande affinché la lista collegata O (1) sia utile.

Un elenco collegato ha un sovraccarico aggiuntivo per i puntatori e le dereferenze successive. Un array ridimensionabile deve copiare i dati in giro. Quella copia è O (n), ma in pratica molto veloce.


1
Una matrice ridimensionabile viene raddoppiata ogni volta che si riempie, quindi il costo medio del ridimensionamento per inserimento è O (1).
Kevin Cline,

2
@kevincline, sì ma la O (n) deriva dal dover spostare tutti gli elementi dopo il punto di inserimento in avanti. L'allocazione è ammortizzata O (1) volta. Il mio punto è che quel movimento è ancora molto veloce, quindi in pratica di solito batte le liste collegate.
Winston Ewert,

La ragione per cui gli array contigui sono così veloci rispetto agli elenchi collegati è dovuta alla memorizzazione nella cache del processore. Attraversare un elenco collegato causerà un errore nella cache per ogni elemento. Per ottenere il meglio da entrambi i mondi è necessario utilizzare un elenco di collegamenti non srotolati .
dan_waterworth,

Le matrici ridimensionabili non vengono sempre copiate. Dipende da cosa sta funzionando e se c'è qualcosa sulla strada. Lo stesso vale per le dimensioni raddoppiate, specifiche per l'implementazione. Il roll over roll over è un problema. Gli elenchi collegati sono in genere i migliori per le code di dimensioni sconosciute, sebbene i buffer rotativi consentano alle code di correre. In altri casi gli elenchi collegati sono utili perché l'allocazione o l'espansione semplicemente non ti consentiranno di avere elementi contigui in ogni momento, quindi avrai comunque bisogno di un puntatore.
jgmjgm,

@jgmjgm, se si inserisce nel mezzo di un array ridimensionabile, ne copia assolutamente gli elementi dopo.
Winston Ewert,

8

La notazione Big-Oh viene utilizzata per descrivere il tasso di crescita di una funzione, quindi è possibile che un algoritmo O (1) sia più veloce, ma solo fino a un certo punto (il fattore costante).

Notazioni comuni:

O (1) - Il numero di iterazioni (a volte è possibile fare riferimento a questo come tempo dell'utente trascorso dalla funzione) non dipende dalla dimensione dell'input ed è infatti costante.

O (n) - Il numero di iterazioni aumenta in proporzione lineare alla dimensione dell'input. Significato: se l'algoritmo scorre attraverso qualsiasi input N, 2 * N volte, viene comunque considerato O (n).

O (n ^ 2) (quadratico) - Il numero di iterazioni è la dimensione di input al quadrato.


2
Per aggiungere un esempio a una risposta altrimenti eccellente: un metodo O (1) può richiedere 37 anni per chiamata, mentre un metodo O (n) può richiedere 16 * n microsecondi per chiamata. Qual è più veloce?
Kaz Dragon

16
Non riesco a vedere come questo risponda alla domanda.
avakar,

7
Capisco big-O. Questo non affronta la domanda reale, che è esempi specifici di funzioni in cui gli algoritmi con un big-O inferiore sono sovraperformati da quelli con un big-O superiore.
KyleWpppd,

Quando metti la domanda nel modulo "Ci sono esempi ...", qualcuno inevitabilmente risponderà "Sì". senza dare alcun.
Rakslice,

1
@rakslice: forse è così. Tuttavia, questo sito richiede una spiegazione (o meglio ancora una prova) delle dichiarazioni che fai. Ora il modo migliore per provare che ci sono esempi del genere è dare uno;)
back2dos

6

Le librerie Regex sono di solito implementate per eseguire il backtracking che presenta tempi esponenziali nel caso peggiore anziché la generazione di DFA che presenta una complessità di O(nm).

Il backtracking ingenuo può essere un performer migliore quando l'input rimane sul percorso veloce o fallisce senza la necessità di backtracking eccessivo.

(Sebbene questa decisione non sia solo basata sulle prestazioni, è anche consentire riferimenti a ritroso).


Penso che sia anche in parte storico: l'algoritmo per trasformare un'espressione regolare in un DFA è stato brevettato quando alcuni degli strumenti precedenti (sed e grep, immagino) venivano sviluppati. Ovviamente ho sentito questo dal mio professore di compilatori che non era del tutto sicuro, quindi questo è un account di terza mano.
Tikhon Jelvis,

5

Un O(1)algoritmo:

def constant_time_algorithm
  one_million = 1000 * 1000
  sleep(one_million) # seconds
end

Un O(n)algoritmo:

def linear_time_algorithm(n)
  sleep(n) # seconds
end

Chiaramente, per qualsiasi valore di ncui n < one_million, O(n)l'algoritmo dato nell'esempio sarà più veloce rispetto al O(1)algoritmo.

Mentre questo esempio è un po 'faceto, è equivalente nello spirito al seguente esempio:

def constant_time_algorithm
  do_a_truckload_of_work_that_takes_forever_and_a_day
end

def linear_time_algorithm(n)
  i = 0
  while i < n
    i += 1
    do_a_minute_amount_of_work_that_takes_nanoseconds
  end
end

tu necessario conoscere le costanti e coefficienti nella vostra Oespressione, e si deve conoscere la gamma prevista n, al fine di determinare a priori quale algoritmo finirà per essere più veloce.

Altrimenti tu necessario confrontare i due algoritmi con valori compresi nnell'intervallo previsto per determinare a posteriori quale algoritmo sia risultato più veloce.


4

Ordinamento:

L'ordinamento per inserzione è O (n ^ 2) ma supera gli altri algoritmi di ordinamento O (n * log (n)) per un numero limitato di elementi.

Questo è il motivo per cui la maggior parte delle implementazioni di ordinamento utilizza una combinazione di due algoritmi. Ad esempio, utilizzare l'ordinamento per unire le grandi matrici fino a quando non raggiungono un array di determinate dimensioni, quindi utilizzare l'ordinamento per inserimento per ordinare le unità più piccole e unirle nuovamente con l'ordinamento per unione.

Vedi Timsort l'attuale implementazione predefinita dell'ordinamento Python e Java 7 che utilizza questa tecnica.



3

Bubblesort in memoria può sovraperformare quicksort quando il programma viene scambiato su disco o è necessario leggere ogni elemento dal disco durante il confronto.

Questo dovrebbe essere un esempio a cui può riferirsi.


Le complessità citate su quicksort e bubblesort non presuppongono l'accesso alla memoria casuale O (1)? Se così non fosse, non sarebbe necessario riesaminare la complessità degli shortsorts?
Viktor Dahl,

@ViktorDahl, il tempo di accesso all'elemento non fa parte di ciò che viene tradizionalmente misurato nelle complessità dell'algoritmo di ordinamento, quindi "O (1)" non è la scelta giusta delle parole qui. Utilizzare invece "tempo costante". PHK ha scritto un articolo sugli algoritmi di ordinamento sapendo che alcuni elementi sono più costosi da recuperare rispetto ad altri (memoria virtuale) - queue.acm.org/detail.cfm?id=1814327 - potresti trovarlo interessante.

Vedo il mio errore ora. Uno di solito misura il numero di confronti e, naturalmente, non sono influenzati dalla velocità del supporto di memorizzazione. Inoltre, grazie per il link.
Viktor Dahl,

3

Spesso gli algoritmi più avanzati presuppongono una certa quantità di configurazione (costosa). Se devi eseguirlo solo una volta, potresti stare meglio con il metodo della forza bruta.

Ad esempio: la ricerca binaria e la ricerca della tabella hash sono entrambe molto più veloci per ricerca una ricerca lineare, ma richiedono di ordinare rispettivamente l'elenco o creare la tabella hash.

L'ordinamento ti costerà N log (N) e la tabella hash costerà almeno N. Ora se hai intenzione di fare centinaia o migliaia di ricerche, è comunque un risparmio ammortizzato. Ma se hai solo bisogno di fare una o due ricerche, potrebbe avere senso semplicemente fare la ricerca lineare e risparmiare i costi di avvio.


1

La decrittazione è spesso 0 (1). Ad esempio, lo spazio chiave per DES è 2 ^ 56, quindi la decrittografia di qualsiasi messaggio è un'operazione a tempo costante. È solo che hai un fattore di 2 ^ 56 lì, quindi è una costante davvero grande.


La decrittazione di un messaggio non è O ( n ), dove n è proporzionale alla dimensione del messaggio? Finché hai la chiave corretta, la dimensione della chiave non prende nemmeno in considerazione; alcuni algoritmi hanno processi di installazione / espansione chiave minimi o assenti (DES, RSA - si noti che la generazione di chiavi può essere ancora un'attività complessa, ma ciò non ha nulla a che fare con l'espansione della chiave) mentre altri sono estremamente complessi (viene in mente Blowfish), ma una volta terminato, il tempo necessario per eseguire il lavoro effettivo è proporzionale alla dimensione del messaggio, quindi O (n).
un CVn

Probabilmente intendi la criptananalisi piuttosto che la decrittazione?
circa il

3
Bene, sì, ci sono un numero qualsiasi di cose che puoi prendere per essere costante e dichiarare un algoritmo come O (1). [l'ordinamento presuppone implicitamente che gli elementi richiedano una quantità costante di tempo per confrontare, ad esempio, o qualsiasi matematica con numeri non bignici]
Casuale 832

1

Mi vengono in mente diverse implementazioni di set. Uno dei più ingenui è implementarlo su un vettore, il che significa removeche anche containse quindi addprendere anche O (N).
Un'alternativa è implementarlo su un hash di uso generale, che associa gli hash di input ai valori di input. Un'implementazione di questo tipo esegue con O (1) per add, containseremove .

Se supponiamo che N sia circa 10, la prima implementazione è probabilmente più veloce. Tutto quello che deve fare per trovare un elemento è confrontare 10 valori con uno.
L'altra implementazione dovrà iniziare ogni sorta di trasformazioni intelligenti, che possono essere molto più costose, rispetto a fare 10 confronti. Con tutto il sovraccarico, potresti anche avere dei cali della cache e quindi non importa davvero quanto velocemente la tua soluzione sia in teoria.

Ciò non significa che l'implementazione peggiore a cui puoi pensare supererà una decente, se N è abbastanza piccolo. Significa semplicemente per N sufficientemente piccolo, che un'implementazione ingenua, con ingombro ridotto e sovraccarico può effettivamente richiedere meno istruzioni e causare meno errori di cache rispetto a un'implementazione che mette al primo posto la scalabilità e sarà quindi più veloce.

Non puoi davvero sapere quanto è veloce qualcosa in uno scenario del mondo reale, fino a quando non lo metti in uno e lo misuri semplicemente. Spesso i risultati sono sorprendenti (almeno per me).


1

Sì, per N. adeguatamente piccolo Ci sarà sempre una N, sopra la quale avrai sempre l'ordinamento O (1) <O (lg N) <O (N) <O (N log N) <O (N ^ c ) <O (c ^ N) (dove O (1) <O (lg N) significa che in un algoritmo O (1) verranno eseguite meno operazioni quando N è adeguatamente grande e c è una costante fissa maggiore di 1 ).

Supponiamo che un particolare algoritmo O (1) esegua esattamente f (N) = 10 ^ 100 (a googol) operazioni e un algoritmo O (N) esegua esattamente g (N) = 2 N + 5 operazioni. L'algoritmo O (N) fornirà prestazioni maggiori finché N non sarà più o meno un googol (in realtà quando N> (10 ^ 100 - 5) / 2), quindi se ti aspettavi che N fosse compreso tra 1000 e un miliardo di subirebbe una penalità maggiore usando l'algoritmo O (1).

O per un confronto realistico, supponiamo che si stiano moltiplicando i numeri di n cifre. L' algoritmo Karatsuba è al massimo 3 n ^ (lg 3) operazioni (ovvero circa O (n ^ 1.585)) mentre l' algoritmo Schönhage – Strassen è O (N log N log log N) che è un ordine più veloce , ma per citare wikipedia:

In pratica l'algoritmo di Schönhage – Strassen inizia a sovraperformare i metodi più vecchi come la moltiplicazione di Karatsuba e Toom – Cook per numeri oltre 2 ^ 2 ^ 15 a 2 ^ 2 ^ 17 (da 10.000 a 40.000 cifre decimali). [4] [5] [6 ]

Quindi, se si moltiplicano i numeri di 500 cifre insieme, non ha senso utilizzare l'algoritmo "più veloce" per grandi argomenti O.

EDIT: Puoi trovare determinare f (N) rispetto a g (N), prendendo il limite N-> infinito di f (N) / g (N). Se il limite è 0, allora f (N) <g (N), se il limite è infinito, allora f (N)> g (N), e se il limite è un'altra costante, allora f (N) ~ g (N) in termini di notazione O grande.


1

Il metodo simplex per la programmazione lineare può essere esponenziale nel peggiore dei casi, mentre algoritmi di punti interni relativamente nuovi possono essere polinomiali.

Tuttavia, in pratica, non si presenta il caso peggiore esponenziale per il metodo simplex: il metodo simplex è veloce e affidabile, mentre i primi algoritmi di punti interni erano troppo lenti per essere competitivi. (Ora ci sono algoritmi di punti interni più moderni che sono competitivi - ma anche il metodo simplex è ...)


0

L'algoritmo di Ukkonen per la creazione di tentativi di suffisso è O (n log n). Ha il vantaggio di essere "on-line", ovvero puoi aggiungere in modo incrementale più testo.

Recentemente, altri algoritmi più complessi hanno affermato di essere più veloci nella pratica, soprattutto perché il loro accesso alla memoria ha una località più elevata, migliorando così l'utilizzo della cache del processore ed evitando le stalle della pipeline della CPU. Vedi, ad esempio, questo sondaggio , che afferma che il 70-80% del tempo di elaborazione viene impiegato in attesa di memoria e questo documento che descrive l'algoritmo "wotd".

I tentativi di suffisso sono importanti in genetica (per abbinare le sequenze geniche) e, un po 'meno importante, nell'implementazione dei dizionari Scrabble.


0

C'è sempre l'algoritmo più veloce e più breve per qualsiasi problema ben definito . Tuttavia, è solo teoricamente l'algoritmo (asintoticamente) più veloce.

Data una descrizione di un problema P e un'istanza per quel problema io , enumera tutte le possibili algoritmi A e prove Pr , controllando per ciascuna di tali coppie se Pr è una prova valida che A è l'algoritmo asintoticamente veloce per P . Se trova una tale prova, che poi esegue una su I .

La ricerca di questa coppia a prova di problema ha una complessità O (1) (per un problema risolto P ), quindi si utilizza sempre l'algoritmo asintoticamente più veloce per il problema. Tuttavia, poiché questa costante è così indicibilmente enorme in quasi tutti i casi, questo metodo è completamente inutile nella pratica.


0

Molti linguaggi / framework usano una corrispondenza dei pattern ingenua per abbinare le stringhe anziché KMP . Cerchiamo stringhe come Tom, New York piuttosto che ababaabababababaababababababab.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.