Cos'è il tempo pseudopolinomiale? In che cosa differisce dal tempo polinomiale?


Risposte:


253

Per capire la differenza tra tempo polinomiale e tempo pseudopolinomiale, dobbiamo iniziare formalizzando il significato di "tempo polinomiale".

L'intuizione comune per il tempo polinomiale è "tempo O (n k ) per qualche k". Ad esempio, l' ordinamento della selezione viene eseguito nel tempo O (n 2 ), che è il tempo polinomiale, mentre la risoluzione della forza bruta TSP richiede il tempo O (n · n!), Che non è il tempo polinomiale.

Questi runtime si riferiscono tutti a una variabile n che tiene traccia della dimensione dell'input. Ad esempio, nell'ordinamento della selezione, n si riferisce al numero di elementi nell'array, mentre in TSP n si riferisce al numero di nodi nel grafico. Al fine di standardizzare la definizione di ciò che "n" effettivamente significa in questo contesto, la definizione formale di complessità temporale definisce la "dimensione" di un problema come segue:

La dimensione dell'input per un problema è il numero di bit richiesti per scrivere quell'input.

Ad esempio, se l'input di un algoritmo di ordinamento è un array di numeri interi a 32 bit, la dimensione dell'input sarebbe 32n, dove n è il numero di voci nell'array. In un grafico con n nodi em archi, l'input potrebbe essere specificato come un elenco di tutti i nodi seguito da un elenco di tutti gli archi, il che richiederebbe Ω (n + m) bit.

Data questa definizione, la definizione formale di tempo polinomiale è la seguente:

Un algoritmo viene eseguito in tempo polinomiale se il suo tempo di esecuzione è O (x k ) per una costante k, dove x indica il numero di bit di input forniti all'algoritmo.

Quando si lavora con algoritmi che elaborano grafici, elenchi, alberi, ecc., Questa definizione è più o meno d'accordo con la definizione convenzionale. Ad esempio, supponi di avere un algoritmo di ordinamento che ordina array di interi a 32 bit. Se usi qualcosa come l'ordinamento della selezione per farlo, il runtime, in funzione del numero di elementi di input nell'array, sarà O (n 2 ). Ma come fa n, il numero di elementi nell'array di input, a corrispondere al numero di bit di input? Come accennato in precedenza, il numero di bit di input sarà x = 32n. Pertanto, se esprimiamo il tempo di esecuzione dell'algoritmo in termini di x anziché n, otteniamo che il tempo di esecuzione è O (x 2 ), e quindi l'algoritmo funziona in tempo polinomiale.

Allo stesso modo, supponiamo di sì una ricerca in profondità su un grafico, che richiede tempo O (m + n), dove m è il numero di archi nel grafico e n è il numero di nodi. In che modo questo si collega al numero di bit di input forniti? Bene, se assumiamo che l'input sia specificato come una lista di adiacenza (una lista di tutti i nodi e archi), allora come accennato in precedenza il numero di bit di input sarà x = Ω (m + n). Pertanto, il runtime sarà O (x), quindi l'algoritmo viene eseguito in tempo polinomiale.

Le cose però si interrompono quando iniziamo a parlare di algoritmi che operano sui numeri. Consideriamo il problema di verificare se un numero è primo o meno. Dato un numero n, puoi verificare se n è primo utilizzando il seguente algoritmo:

function isPrime(n):
    for i from 2 to n - 1:
        if (n mod i) = 0, return false
    return true

Allora qual è la complessità temporale di questo codice? Bene, quel ciclo interno viene eseguito O (n) volte e ogni volta fa una certa quantità di lavoro per calcolare n mod i (come limite superiore molto conservativo, questo può certamente essere fatto nel tempo O (n 3 )). Pertanto, questo algoritmo complessivo viene eseguito nel tempo O (n 4 ) e forse molto più velocemente.

Nel 2004, tre scienziati informatici hanno pubblicato un documento chiamato PRIMES is in P che fornisce un algoritmo tempo polinomiale per verificare se un numero è primo. È stato considerato un risultato fondamentale. Allora qual è il grosso problema? Non abbiamo già un algoritmo tempo polinomiale per questo, vale a dire quello sopra?

Purtroppo no. Ricorda, la definizione formale di complessità temporale parla della complessità dell'algoritmo in funzione del numero di bit di input. Il nostro algoritmo funziona nel tempo O (n 4 ), ma cos'è questo in funzione del numero di bit di input? Ebbene, scrivere il numero n richiede O (log n) bit. Pertanto, se si lascia x il numero di bit necessari per scrivere l'input n, il tempo di esecuzione di questo algoritmo è in realtà O (2 4x ), che non è un polinomio in x.

Questo è il cuore della distinzione tra tempo polinomiale e tempo pseudopolinomiale. Da un lato, il nostro algoritmo è O (n 4 ), che sembra un polinomio, ma dall'altra, sotto la definizione formale di tempo polinomiale, non è tempo polinomiale.

Per avere un'idea del motivo per cui l'algoritmo non è un algoritmo tempo polinomiale, pensa a quanto segue. Supponiamo che io voglia che l'algoritmo debba fare molto lavoro. Se scrivo un input come questo:

10001010101011

quindi ci vorrà un po 'di tempo nel peggiore dei casi, diciamo T, per completare. Se ora aggiungo un singolo bit alla fine del numero, in questo modo:

100010101010111

Il runtime ora (nel peggiore dei casi) sarà 2T. Posso raddoppiare la quantità di lavoro che l'algoritmo fa semplicemente aggiungendo un altro bit!

Un algoritmo viene eseguito in tempo pseudopolinomiale se il runtime è un polinomio nel valore numerico dell'input , piuttosto che nel numero di bit richiesti per rappresentarlo. Il nostro algoritmo di test principale è un algoritmo temporale pseudopolinomiale, poiché viene eseguito nel tempo O (n 4 ), ma non è un algoritmo tempo polinomiale perché in funzione del numero di bit x richiesti per scrivere l'input, il tempo di esecuzione è O (2 4x ). Il motivo per cui il documento "PRIMES is in P" era così significativo era che il suo tempo di esecuzione era (approssimativamente) O (log 12 n), che in funzione del numero di bit è O (x 12 ).

Allora perché è importante? Bene, abbiamo molti algoritmi temporali pseudopolinomiali per la fattorizzazione di interi. Tuttavia, questi algoritmi sono, tecnicamente parlando, algoritmi in tempo esponenziale. Questo è molto utile per la crittografia: se vuoi usare la crittografia RSA, devi essere in grado di credere che non possiamo fattorizzare facilmente i numeri. Aumentando il numero di bit nei numeri a un valore enorme (ad esempio, 1024 bit), è possibile rendere la quantità di tempo che l'algoritmo di fattorizzazione del tempo pseudopolinomiale deve prendere diventa così grande che sarebbe completamente e assolutamente impossibile fattorizzare il numeri. Se invece possiamo trovare un polinomio algoritmo di factoring tempo, non è necessariamente così. L'aggiunta di più bit può far crescere molto il lavoro, ma la crescita sarà solo una crescita polinomiale, non una crescita esponenziale.

Detto questo, in molti casi gli algoritmi del tempo pseudopolinomiale vanno benissimo perché la dimensione dei numeri non sarà troppo grande. Ad esempio, il conteggio dell'ordinamento ha runtime O (n + U), dove U è il numero più grande dell'array. Questo è il tempo pseudopolinomiale (poiché il valore numerico di U richiede la scrittura di bit O (log U), quindi il runtime è esponenziale nella dimensione dell'input). Se vincoliamo artificialmente U in modo che U non sia troppo grande (diciamo, se lasciamo U 2), il tempo di esecuzione è O (n), che in realtà è il tempo polinomiale. Questo è il modo in cui radix ordina il funziona l' : elaborando i numeri un bit alla volta, il tempo di esecuzione di ogni round è O (n), quindi il tempo di esecuzione complessivo è O (n log U). Questo in realtà lo è tempo polinomiale, perché la scrittura di n numeri da ordinare utilizza Ω (n) bit e il valore di log U è direttamente proporzionale al numero di bit necessari per scrivere il valore massimo nell'array.

Spero che questo ti aiuti!


27
Questa dovrebbe essere la spiegazione di Wikipedia ...
Sandro Meier

4
Perché isPrimela complessità di s è stimata come O (n ^ 4) e non semplicemente O (n)? Non lo capisco. A meno che la complessità di n mod isia O (n ^ 3) .... che sicuramente non lo è.
fons

4
@ Nessuno Normalmente pensiamo al costo di modificare due numeri come O (1), ma quando hai a che fare con numeri arbitrariamente grandi, il costo per fare la moltiplicazione aumenta in funzione della dimensione dei numeri stessi. Per essere estremamente prudente, ho affermato che puoi calcolare il modding con un numero inferiore a n come O (n ^ 3), che è un conteggio grossolano ma non troppo male.
templatetypedef

1
@AndrewFlemming Dipende da come il numero è rappresentato in memoria. Stavo assumendo che stessimo usando una rappresentazione binaria standard, in cui avremmo bisogno di log_2 n bit per rappresentare il numero. Tuttavia, hai ragione sul fatto che la modifica della rappresentazione sottostante cambierà il runtime in funzione della dimensione dell'input.
templatetypedef

1
Scegliere O (n ^ 3) per n mod iè eccessivamente conservativo. La tempistica di modè una funzione del numero di bit n, non di nse stessa, quindi dovrebbe essere O ((log n) ^ 3).
dasblinkenlight

2

Per complessità temporale pseudo-polinomiale si intende polinomiale nel valore / grandezza dell'input ma esponenziale nella dimensione dell'input.

Per dimensione si intende il numero di bit necessari per scrivere l'input.

Dallo pseudo-codice di zaino, possiamo trovare la complessità temporale come O (nW).

// Input:
// Values (stored in array v) 
// Weights (stored in array w)
// Number of distinct items (n) //
Knapsack capacity (W) 
for w from 0 to W 
    do   m[0, w] := 0 
end for  
for i from 1 to n do  
        for j from 0 to W do
               if j >= w[i] then 
                      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i]) 
              else 
                      m[i, j] := m[i-1, j]
              end if
       end for 
end for

In questo caso, W non è polinomiale nella lunghezza dell'input, il che è ciò che lo rende pseudo-polinomiale.

Sia il numero di bit necessari per rappresentare W

i.e. size of input= s =log(W) (log= log base 2)
-> 2^(s)=2^(log(W))
-> 2^(s)=W  (because  2^(log(x)) = x)

Ora, running time of knapsack= O (nW) = O (n * 2 ^ s) che non è polinomiale.

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.