Qual è la complessità temporale di questo algoritmo? E perché?


8

Sono bloccato analizzando la complessità temporale del seguente algoritmo:

def fun (r, k, d, p):
    if d > p:
        return r
    if d = 0 and p = 0:
        r <- r + k
        return r
    if d > 0:
        fun (r, k + 1, d - 1, p)
    if p > 0:
        fun (r, k - 1, d, p - 1)

La chiamata root sarà fun (0, 0, n, n)ed nè la dimensione del problema.

Immagino che: la relazione di ricorrenza sia , che equivale a , e quindi .T(n,n)=T(n1,n)+T(n,n1)T(2n)=2T(2n1)T(m)=2T(m1)O(2m)O(4n)

La mia analisi è corretta (so che non è molto completa ed esatta)? Se presenta un difetto grave, segnalalo o mostrami una prova corretta e completa sulla complessità temporale di questo algoritmo.


1
xando, ti incoraggio a modificare la domanda per spiegare perché le tecniche standard non funzionano: spiega perché falliscono. (Cc: @YuvalFilmus) Ad esempio, ricevi una relazione di ricorrenza che è difficile da risolvere e, in tal caso, quale ricorrenza ottieni?
DW

1
Nei commenti con Polyergic, mi sono reso conto che lo pseudocodice non è chiaro: non è chiaro cosa intendi per l'algoritmo, quando entrambi d>0e p>0. Non mostri cosa restituisce la funzione se raggiungiamo le istruzioni if ​​3 e 4th. Intendevi avere una returndichiarazione dopo ogni invocazione ricorsiva di fun? (intendevi fun (r, k + 1, d - 1, p)essere return fun (r, k + 1, d - 1, p)?) O intendevi avere returnun'affermazione alla fine del corpo della funzione? Modifica il tuo pseudocodice per chiarire e assicurarti di mostrare ciò che restituisce in tutti i casi possibili.
DW

Per dirlo in un altro modo: supponiamo d<=pe d>0e p>0tutto tenga. Cosa dovrebbe succedere? L'algoritmo esegue 2 invocazioni ricorsive alla funzione? O invoca ricorsivamente fun(r, k + 1, d - 1, p)e poi immediatamente ritorna, senza invocare ricorsivamente fun(r, k - 1, d, p - 1)? Se prendo il tuo pseudocodice alla lettera, sembra che compia 2 invocazioni ricorsive e poi ritorni con un valore di ritorno indefinito, ma questo sembra strano e mi chiedo se ci sia un errore di battitura / bug nello pseudocodice.
DW

Risposte:


10

Gli unici due argomenti rilevanti per l'analisi asintotica sono d e p. Questi argomenti (virtualmente) soddisfanod,p0 e dp(abbiamo bisogno di mescolare leggermente la logica nella funzione per ottenere questo). Ad ogni punto dell'esecuzione, prendi la coppia corrente(d,p) e quindi ricorsivamente chiama la funzione con le coppie (d1,p),(d,p1), evitando coppie che invalidano i vincoli sopra indicati.

Possiamo immaginare l'albero delle chiamate risultante come percorso che inizia a (0,0). Ogni volta che diminuiscip, aggiungi a / step. Ogni volta che diminuiscid, aggiungi un \ step. La condizionedpgarantisce che non si scende mai sotto l'asse X. Inoltre, hai un "budget" dindi ogni passaggio. Il numero totale di foglie in questo albero delle chiamate è esattamente il numero catalano , e questo ci dà un limite inferiore sul tempo di esecuzione della funzione.(2nn)/(n+1)=Θ(4n/n3/2)

Per ottenere un limite superiore, nota che sulla strada per ogni foglia passiamo attraverso nodi, e questo dà un limite superiore di più grande del limite inferiore, cioè, .2n2nΘ(4n/n)

Abbiamo un limite inferiore di e un limite superiore su . Quali sono gli esotici asintotici? Crescono come il numero totale di percorsi che non attraversano l'asse X che hanno al massimo passi in ciascuna direzione. Usando il teorema del voto di Bertrand possiamo ottenere un'espressione esatta per questo: Resta quindi da stimare asintoticamente questa somma: Ω(4n/n3/2)O(4n/n)n

0dpnpd+1p+1(p+dp).
0dpn(p+dp)0dpndp+1(p+dd)=0dpn(p+dp)0dpn(p+dp+1)=p=0n(2p+1p+1)p=0n(2p+1p+2)=p=0n1p+1(2p+2p)=Θ(p=0n4pp3/2)=Θ(4nn3/2).

1
Mi piace molto il tuo approccio geometrico usando questi "passaggi". È una tecnica comune? Non l'ho mai visto prima
Andreas T,

@AndreasT Non la definirei una tecnica comune, anzi normalmente non si applica. Qui l'interpretazione combinatoria è piuttosto evidente e conduce a questo tipo di soluzione.
Yuval Filmus,

0

Caso per caso:

  1. d> p : tempo costante
  2. d = 0 ∧ p = 0 : tempo costante
  3. d> 0 : Nota che d ≯ p, quindi abbiamo 0 <d ≤ p, e funricorre su d-1 fino a d ≯ 0; poiché p> 0, questo è lineare in d + (caso 4) .
  4. p> 0 : Nota che d ≯ 0, quindi abbiamo d ≤ 0 ≤ p (con d <p), e funricorre su p-1 fino a p ≯ 0; questo è lineare in p + (uno dei casi 1, 2 o 5)
  5. d ≤ p <0 : non definito; Suppongo che questo sia tempo costante

A partire da d = p = n> 0 indica il caso 3, seguito dal caso 4. Se n è un numero intero, il caso finale è 2, altrimenti il ​​caso finale è 5. Il tempo totale per tali casi è d + p +1 o 2n + 1.


2n. Immagino che tu abbia votato verso il basso perché mi sono concentrato sul ragionamento?
ShadSterling

1
Grazie per aver modificato la risposta! Il puzzle ora è che hai concluso che il tempo di esecuzione è , ma Yuval ha concluso che il tempo di esecuzione è esponenziale in . Questa è una differenza abbastanza sostanziale. O(n)n
DW

1
Hmm, penso di aver preso lo pseudocodice ifcome "fai uno di questi" mentre @Yuval li ha considerati "considera ognuno di questi in ordine". Quest'ultimo è, ovviamente, ciò che ifs (senza else) significa nel codice reale; Sono abituato al primo in quasi tutti gli altri contesti rispetto al codice reale (incluso in pseudocodice, sebbene l'utilizzo in pseudocodice sia incoerente).
ShadSterling

Capisco cosa intendi. La mancanza di returndichiarazioni nella seconda metà del codice rende questo piuttosto confuso.
DW
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.