Qual è la ricorsione e quando dovrei utilizzarlo?

voti
121

Uno dei temi che sembra venire regolarmente su mailing list e discussioni on-line è il merito (o assenza) di fare un grado di scienza del computer. Un argomento che sembra venire il tempo e di nuovo per il partito negativo è che essi sono stati codifica per un determinato numero di anni e non hanno mai utilizzato la ricorsione.

Quindi la domanda è:

  1. Che cosa è la ricorsione?
  2. Quando si usa la ricorsione?
  3. Perché la gente non utilizzare la ricorsione?
È pubblicato 06/08/2008 alle 03:29
fonte dall'utente
In altre lingue...                            


40 risposte

voti
86

Ci sono una serie di buone spiegazioni di ricorsione in questa discussione, questa risposta è di circa il motivo per cui non si dovrebbe usare nella maggior parte delle lingue. * Nella maggior parte delle grandi implementazioni linguaggio imperativo (vale a dire tutti i principali implementazione di C, C ++, di base, Python , Ruby, Java e C #) iterazione è di gran lunga preferibile alla ricorsione.

Per capire perché, a piedi, attraverso i passi che le lingue di cui sopra usano per chiamare una funzione:

  1. lo spazio è scavata sul nello stack per argomenti della funzione e le variabili locali
  2. argomenti della funzione vengono copiati in questo nuovo spazio
  3. il controllo passa alla funzione
  4. esegue il codice della funzione
  5. il risultato della funzione viene copiato in un valore di ritorno
  6. la pila viene riavvolto nella posizione precedente
  7. controllo ritorna al punto in cui è stata chiamata la funzione

Facendo tutti questi passaggi richiede tempo, di solito un po 'di più di quello necessario per scorrere un ciclo. Tuttavia, il vero problema è nel passo # 1. Quando molti programmi avviare, essi allocare un singolo pezzo di memoria per la pila, e quando hanno finito di quella memoria (spesso, ma non sempre causa di ricorsione), il programma si blocca a causa di un overflow dello stack .

Quindi, in queste lingue ricorsione è più lento e ti rende vulnerabile a schiantarsi. Ci sono ancora alcuni argomenti per il suo utilizzo però. In generale, il codice scritto in modo ricorsivo è più breve e un po 'più elegante, una volta che sai come leggerlo.

V'è una tecnica che gli esecutori di lingue possono utilizzare chiamato ottimizzazione chiamata coda che può eliminare alcune classi di overflow dello stack. Metti succintamente: se l'espressione il ritorno di una funzione è semplicemente il risultato di una chiamata di funzione, quindi non è necessario aggiungere un nuovo livello nello stack, è possibile riutilizzare quello corrente per la funzione di essere chiamato. Purtroppo, alcuni imperativi lingua-implementazioni hanno ottimizzazione coda-call costruito.

* Mi piace la ricorsione. Il mio linguaggio statico preferito non utilizzare i loop a tutti, la ricorsione è l'unico modo per fare qualcosa ripetutamente. Io non credo che la ricorsione è generalmente una buona idea in lingue che non sono sintonizzati per essa.

** Tra l'altro Mario, il nome tipico per la funzione ArrangeString è "join", e sarei sorpreso se la vostra lingua scelta non dispone già di un'implementazione di essa.

Risposto il 06/08/2008 a 06:09
fonte dall'utente

voti
63

Semplice esempio inglese della ricorsione.

A child couldn't sleep, so her mother told her a story about a little frog,
    who couldn't sleep, so the frog's mother told her a story about a little bear,
         who couldn't sleep, so the bear's mother told her a story about a little weasel... 
            who fell asleep.
         ...and the little bear fell asleep;
    ...and the little frog fell asleep;
...and the child fell asleep.
Risposto il 04/05/2010 a 17:38
fonte dall'utente

voti
49

Nella più elementare senso di informatica, la ricorsione è una funzione che si chiama. Diciamo che avete una struttura lista collegata:

struct Node {
    Node* next;
};

E volete sapere quanto tempo una lista collegata è che si può fare questo con la ricorsione:

int length(const Node* list) {
    if (!list->next) {
        return 1;
    } else {
        return 1 + length(list->next);
    }
}

(Ciò può naturalmente essere fatto con un ciclo pure, ma è utile come illustrazione del concetto)

Risposto il 04/05/2010 a 12:25
fonte dall'utente

voti
46

Ogni volta che una funzione chiama se stessa, creando un ciclo, allora questo è ricorsione. Come con qualsiasi cosa ci sono buoni usi e usi male per la ricorsione.

L'esempio più semplice è coda ricorsione dove l'ultima riga della funzione è una chiamata a se stesso:

int FloorByTen(int num)
{
    if (num % 10 == 0)
        return num;
    else
        return FloorByTen(num-1);
}

Tuttavia, questo è un zoppo, esempio quasi inutile perché può essere facilmente sostituito da iterazione più efficiente. Dopo tutto, ricorsione soffre funzione di chiamata sovraccarico, che nell'esempio di cui sopra potrebbe essere notevole rispetto al funzionamento all'interno della funzione stessa.

Così tutta la ragione per fare la ricorsione invece di iterazione dovrebbe essere quello di sfruttare la stack di chiamate a fare alcune cose intelligenti. Ad esempio, se si chiama una funzione più volte con parametri diversi all'interno dello stesso ciclo allora questo è un modo per realizzare ramificazione . Un esempio classico è il triangolo Sierpinski .

entrare descrizione dell'immagine qui

È possibile disegnare uno di quelli molto semplice con la ricorsione, dove i rami stack di chiamate in 3 direzioni:

private void BuildVertices(double x, double y, double len)
{
    if (len > 0.002)
    {
        mesh.Positions.Add(new Point3D(x, y + len, -len));
        mesh.Positions.Add(new Point3D(x - len, y - len, -len));
        mesh.Positions.Add(new Point3D(x + len, y - len, -len));
        len *= 0.5;
        BuildVertices(x, y + len, len);
        BuildVertices(x - len, y - len, len);
        BuildVertices(x + len, y - len, len);
    }
}

Se si tenta di fare la stessa cosa con l'iterazione Penso che troverete ci vuole più codice molto da realizzare.

Altri casi di uso comune possono includere le gerarchie che attraversano, ad esempio, crawler sito web, il confronto di directory, ecc

Conclusione

In termini pratici, la ricorsione rende più senso quando ne hai bisogno ramificazione iterativo.

Risposto il 04/05/2010 a 14:33
fonte dall'utente

voti
28

La ricorsione è un metodo di risoluzione dei problemi in base al divide et impera mentalità. L'idea di base è che si prende il problema originale e dividerlo in più piccole (più facilmente risolti) istanze di se stesso, risolvere quei casi più piccoli (di solito utilizzando di nuovo lo stesso algoritmo) e poi rimontare nella soluzione finale.

L'esempio canonico è una routine per generare il fattoriale di n. Il fattoriale di n è calcolato moltiplicando tutti i numeri tra 1 e n. Una soluzione iterativa in C # è simile al seguente:

public int Fact(int n)
{
  int fact = 1;

  for( int i = 2; i <= n; i++)
  {
    fact = fact * i;
  }

  return fact;
}

Non c'è nulla di sorprendente la soluzione iterativa e dovrebbe dare un senso a chiunque abbia familiarità con C #.

La soluzione ricorsiva è trovato riconoscendo che all'ennesimo fattoriale è n * Fact (n-1). O, per dirla in altro modo, se si sa cosa un particolare numero fattoriale è che si può calcolare il prossimo. Ecco la soluzione ricorsiva in C #:

public int FactRec(int n)
{
  if( n < 2 )
  {
    return 1;
  }

  return n * FactRec( n - 1 );
}

La prima parte di questa funzione è conosciuto come un caso base (o, talvolta, Guardia clausola) ed è ciò che impedisce l'algoritmo di esecuzione per sempre. Semplicemente restituisce il valore 1 ogni volta che la funzione viene chiamata con un valore di 1 o meno. La seconda parte è più interessante ed è conosciuto come il Passo ricorsivo . Qui noi chiamiamo lo stesso metodo con un parametro leggermente modificata (noi farlo diminuire 1) e poi moltiplicare il risultato con la nostra copia di n.

La prima volta incontrato questo può essere una specie di confusione quindi è istruttivo esaminare come funziona quando viene eseguito. Immaginate che chiamiamo FactRec (5). Entriamo la routine, non vengono prelevati dal caso base e così finiamo in questo modo:

// In FactRec(5)
return 5 * FactRec( 5 - 1 );

// which is
return 5 * FactRec(4);

Se si rientra il metodo con il parametro 4 non siamo nuovamente fermati dalla clausola guardia e così si finisce a:

// In FactRec(4)
return 4 * FactRec(3);

Se sostituiamo il valore di ritorno nel valore di ritorno di cui sopra otteniamo

// In FactRec(5)
return 5 * (4 * FactRec(3));

Questo dovrebbe dare un indizio su come la soluzione finale è arrivato a così saremo Fast Track e mostrare ogni passo sulla strada verso il basso:

return 5 * (4 * FactRec(3));
return 5 * (4 * (3 * FactRec(2)));
return 5 * (4 * (3 * (2 * FactRec(1))));
return 5 * (4 * (3 * (2 * (1))));

Tale sostituzione finale succede quando il caso base viene attivato. A questo punto abbiamo una semplice formula algrebraic risolvere che corrisponde direttamente alla definizione di fattoriale in primo luogo.

E 'istruttivo il fatto che ogni chiamata nei risultati del metodo sia in un caso base viene attivato o una chiamata al lo stesso metodo in cui i parametri sono più vicini ad un caso base (spesso chiamato una chiamata ricorsiva). Se questo non è il caso, allora il metodo verrà eseguito sempre.

Risposto il 06/08/2008 a 03:54
fonte dall'utente

voti
12

La ricorsione è risolvere un problema con una funzione che chiama se stessa. Un buon esempio di questo è una funzione fattoriale. Fattoriale è un problema di matematica in cui fattoriale di 5, ad esempio, è 5 * 4 * 3 * 2 * 1. Questa funzione risolve questo in C # per gli interi positivi (non testato - ci può essere un bug).

public int Factorial(int n)
{
    if (n <= 1)
        return 1;

    return n * Factorial(n - 1);
}
Risposto il 04/05/2010 a 12:29
fonte dall'utente

voti
9

Si consideri un vecchio, ben noto problema :

In matematica, il massimo comun divisore (gcd) ... di due o più numeri interi diversi da zero, è il più grande intero positivo che divide i numeri senza resto.

La definizione di gcd è sorprendentemente semplice:

definizione GCD

dove mod è l' operatore modulo (cioè, il resto della divisione intera).

In inglese, questa definizione dice il massimo comun divisore di qualsiasi numero e zero è quel numero, e il massimo comun divisore di due numeri m ed n è il massimo comun divisore di n ed il resto dopo la divisione m dal n .

Se vuoi sapere perché questo funziona, vedere l'articolo di Wikipedia sulla algoritmo di Euclide .

Diamo calcolare MCD (10, 8) come esempio. Ogni passo è uguale a quello appena prima che:

  1. gcd (10, 8)
  2. gcd (10, 10 mod 8)
  3. gcd (8, 2)
  4. gcd (8, 8 mod 2)
  5. gcd (2, 0)
  6. 2

Nella prima fase, 8 non è uguale a zero, per cui la seconda parte della definizione applica. 10 mod 8 = 2 perché 8 va in 10 una volta con un resto di 2. Nella fase 3, la seconda parte si applica nuovamente, ma questa volta 8 mod 2 = 0 perchè 2 divide 8 senza resto. Nel passo 5, il secondo argomento è 0, quindi la risposta è 2.

Avete notato che GCD appare su entrambi i lati sinistro e destro del segno di uguale? Un matematico direbbe questa definizione è ricorsiva perché l'espressione che si sta definendo è ricorrente all'interno della sua definizione.

definizioni ricorsive tendono ad essere elegante. Ad esempio, una definizione ricorsiva per la somma di una lista è

sum l =
    if empty(l)
        return 0
    else
        return head(l) + sum(tail(l))

dove headè il primo elemento in un elenco e tailil resto della lista. Si noti che sumricorre all'interno la sua definizione alla fine.

Forse si preferisce il valore massimo in una lista, invece:

max l =
    if empty(l)
        error
    elsif length(l) = 1
        return head(l)
    else
        tailmax = max(tail(l))
        if head(l) > tailmax
            return head(l)
        else
            return tailmax

È possibile definire la moltiplicazione di numeri interi non negativi ricorsivo per trasformarlo in una serie di aggiunte:

a * b =
    if b = 0
        return 0
    else
        return a + (a * (b - 1))

Se quel po 'di trasformare la moltiplicazione in una serie di aggiunte non ha senso, provare ampliando alcuni semplici esempi per vedere come funziona.

Merge sort ha una bella definizione ricorsiva:

sort(l) =
    if empty(l) or length(l) = 1
        return l
    else
        (left,right) = split l
        return merge(sort(left), sort(right))

Definizioni ricorsive sono tutti intorno a se si sa cosa cercare. Notate come tutte queste definizioni hanno casi di base molto semplici, per esempio , MCD (m, 0) = m. I casi ricorsive whittle via il problema per arrivare fino alle risposte facili.

Con questa comprensione, è ora possibile apprezzare le altre algoritmi in articolo di Wikipedia sulla ricorsione !

Risposto il 04/05/2010 a 14:58
fonte dall'utente

voti
9

Ricorsione si riferisce ad un procedimento che risolve un problema risolvendo una versione più piccola del problema e quindi utilizzando tale risultato più qualche altro calcolo per formulare la risposta al problema originale. Spesso, nel processo di risolvere la versione più piccola, il metodo risolverà una versione ancora più piccola del problema, e così via, fino a raggiungere un "caso base" che è banale da risolvere.

Per esempio, per calcolare un fattoriale per il numero X, si può rappresentare come X times the factorial of X-1. Così, il metodo del "recurses" per trovare il fattoriale di X-1, e poi moltiplica tutto ciò che ha da Xdare una risposta definitiva. Naturalmente, per trovare il fattoriale di X-1, sarà prima calcolare il fattoriale di X-2, e così via. Il caso base sarebbe quando Xè 0 o 1, nel qual caso si sa ritorno 1dal 0! = 1! = 1.

Risposto il 04/05/2010 a 12:26
fonte dall'utente

voti
9
  1. Una funzione che si chiama
  2. Quando una funzione può essere (facilmente) decomposto in una semplice operazione più la stessa funzione su una porzione più piccola del problema. Dovrei dire, piuttosto, che questo lo rende un buon candidato per la ricorsione.
  3. Loro fanno!

L'esempio canonico è il fattoriale che assomiglia a:

int fact(int a) 
{
  if(a==1)
    return 1;

  return a*fact(a-1);
}

In generale, la ricorsione non è necessariamente veloce (chiamata di funzione tende ad essere elevato perché funzioni ricorsive tendono ad essere piccole, vedi sopra) e può soffrire di alcuni problemi (overflow dello stack chiunque?). Alcuni dicono che tendono ad essere difficile ottenere 'destra' in casi non banali, ma in realtà non comprare in quello. In alcune situazioni, ricorsione rende più senso ed è il modo più elegante e chiaro per scrivere una particolare funzione. Va notato che alcune lingue favoriscono soluzioni ricorsive e ottimizzare le loro molto più (LISP viene in mente).

Risposto il 06/08/2008 a 03:35
fonte dall'utente

voti
7

Una funzione ricorsiva è quella che si definisce. Il motivo più comune che ho trovato per usarlo sta attraversando una struttura ad albero. Per esempio, se ho un TreeView con caselle di controllo (si pensi l'installazione di un nuovo programma, "scegliere le caratteristiche da installare" pagina), potrei voler un pulsante "controllare tutti", che sarebbe qualcosa di simile a questo (pseudocodice):

function cmdCheckAllClick {
    checkRecursively(TreeView1.RootNode);
}

function checkRecursively(Node n) {
    n.Checked = True;
    foreach ( n.Children as child ) {
        checkRecursively(child);
    }
}

Così si può vedere che la checkRecursively controlla prima il nodo che è passato, poi richiama sé stessa per ognuno dei figli di quel nodo.

Si ha bisogno di essere un po 'attenti con la ricorsione. Se si entra in un ciclo ricorsivo infinito, si otterrà un'eccezione Stack Overflow :)

Non riesco a pensare a una ragione per cui la gente non dovrebbe usare, se del caso. E 'utile in alcune circostanze, e non in altri.

Penso che perché è una tecnica interessante, alcuni programmatori forse finiscono per usarlo più spesso di quanto dovrebbero, senza una reale giustificazione. Questo ha dato la ricorsione una cattiva reputazione in alcuni ambienti.

Risposto il 06/08/2008 a 03:44
fonte dall'utente

voti
5

La ricorsione è un'espressione fa riferimento direttamente o indirettamente in sé.

Considerare acronimo ricorsivo come un semplice esempio:

  • GNU sta per GNU Non è Unix
  • PHP sta per PHP: Hypertext Preprocessor
  • YAML sta per YAML non è Markup Language
  • VINO acronimo di Wine Is Not an Emulator
  • VISTO l'acronimo di Visa International Service Association

Altri esempi su Wikipedia

Risposto il 04/05/2010 a 12:56
fonte dall'utente

voti
5

Ecco un semplice esempio: quanti elementi in un set. (Ci sono modi migliori per contare le cose, ma questo è un bel esempio semplice ricorsivo.)

In primo luogo, abbiamo bisogno di due regole:

  1. se l'insieme è vuoto, il numero di elementi nel set è zero (duh!).
  2. se l'insieme non è vuota, il conteggio è uno più il numero di elementi nella serie dopo un elemento viene rimosso.

Supponiamo di avere un set come questo: [xxx]. contiamo quanti elementi ci sono.

  1. l'insieme è [xxx] che non è vuoto, in modo da applicare regola 2. il numero di elementi è uno più il numero di elementi in [xx] (cioè abbiamo rimosso una voce).
  2. il set è [xx], in modo che applicare l'articolo 2 di nuovo: il numero uno + di articoli in [x].
  3. il set è [x], che corrisponde ancora Regola 2: un numero + di articoli in [].
  4. Ora il set è [], che corrisponde alla regola 1: il conteggio è pari a zero!
  5. Ora che sappiamo che la risposta al punto 4 (0), possiamo risolvere il passaggio 3 (1 + 0)
  6. Allo stesso modo, ora che conosciamo la risposta nella fase 3 (1), possiamo risolvere passaggio 2 (1 + 1)
  7. E finalmente ora che sappiamo la risposta al punto 2 (2), siamo in grado di risolvere il punto 1 (1 + 2) e ottenere il conteggio di elementi in [xxx], che è 3. Hooray!

Possiamo rappresentare questo come:

count of [x x x] = 1 + count of [x x]
                 = 1 + (1 + count of [x])
                 = 1 + (1 + (1 + count of []))
                 = 1 + (1 + (1 + 0)))
                 = 1 + (1 + (1))
                 = 1 + (2)
                 = 3

Quando si applica una soluzione ricorsiva, di solito si hanno almeno 2 regole:

  • la base, il semplice caso in cui si afferma che cosa succede quando si ha "esaurito" di tutti i dati. Questo è di solito una certa variazione di "se si è fuori dei dati da elaborare, la vostra risposta è X"
  • la regola ricorsiva, in cui si afferma che cosa succede se si dispone ancora dei dati. Questo è di solito un qualche tipo di regola che dice "fare qualcosa per rendere il vostro set di dati più piccolo, e riapplicare le regole per il set di dati più piccolo."

Se traduciamo quanto sopra per pseudocodice, otteniamo:

numberOfItems(set)
    if set is empty
        return 0
    else
        remove 1 item from set
        return 1 + numberOfItems(set)

C'è molto esempi più utili (che attraversano un albero, per esempio) che sono sicuro che altre persone copriranno.

Risposto il 06/08/2008 a 04:12
fonte dall'utente

voti
5

Ricorsione funziona meglio con quello che mi piace chiamare "problemi frattali", dove hai a che fare con una grande cosa che è fatta di versioni più piccole di quella grande cosa, ognuno dei quali è una versione ancora più piccola della cosa grande, e così via. Se avete mai attraversare o la ricerca in qualcosa di simile a un albero o strutture identiche nidificate, hai un problema che potrebbe essere un buon candidato per la ricorsione.

Le persone a evitare la ricorsione per una serie di motivi:

  1. La maggior parte delle persone (me compreso) hanno tagliato il loro denti programmazione su programmazione procedurale o orientata agli oggetti al contrario di programmazione funzionale. Per queste persone, l'approccio iterativo (tipicamente utilizzando i cicli) si sente più naturale.

  2. Quelli di noi che ha tagliato i nostri denti di programmazione su programmazione procedurale o orientata agli oggetti sono stati spesso detto di evitare la ricorsione, perché è soggetto a errori.

  3. Siamo spesso detto che la ricorsione è lento. Chiamata e ritorno da una routine più volte comporta un sacco di stack di spingere e popping, che è più lento di loop. Credo che alcune lingue gestire questo meglio di altri, e le lingue sono molto probabilmente non quelli in cui il paradigma dominante è procedurale o orientata agli oggetti.

  4. Per almeno un paio di linguaggi di programmazione che ho usato, mi ricordo di aver sentito le raccomandazioni di non utilizzare la ricorsione se ottiene al di là di una certa profondità, perché la sua pila non è così profondo.

Risposto il 06/08/2008 a 04:12
fonte dall'utente

voti
4

1.) Un metodo è ricorsivo se può definirsi; sia direttamente:

void f() {
   ... f() ... 
}

o indirettamente:

void f() {
    ... g() ...
}

void g() {
   ... f() ...
}

2.) Quando usare la ricorsione

Q: Does using recursion usually make your code faster? 
A: No.
Q: Does using recursion usually use less memory? 
A: No.
Q: Then why use recursion? 
A: It sometimes makes your code much simpler!

3.) Le persone usano la ricorsione solo quando è molto complesso per scrivere codice iterativo. Ad esempio, le tecniche di attraversamento di alberi come il preordine, postorder possono essere fatte sia iterativo e ricorsivo. Ma di solito usiamo ricorsiva per la sua semplicità.

Risposto il 11/03/2014 a 10:47
fonte dall'utente

voti
4

Una dichiarazione ricorsiva è quella in cui si definisce il processo di cosa fare dopo come una combinazione di ingressi e di quello che hai già fatto.

Per esempio, prendiamo fattoriale:

factorial(6) = 6*5*4*3*2*1

Ma è facile vedere fattoriale (6) è anche:

6 * factorial(5) = 6*(5*4*3*2*1).

Quindi in generale:

factorial(n) = n*factorial(n-1)

Naturalmente, la cosa più difficile di ricorsione è che se si vuole definire le cose in termini di quello che hai già fatto, ci deve essere un certo punto di partenza.

In questo esempio, facciamo solo un caso particolare definendo fattoriale (1) = 1.

Ora lo vediamo dal basso verso l'alto:

factorial(6) = 6*factorial(5)
                   = 6*5*factorial(4)
                   = 6*5*4*factorial(3) = 6*5*4*3*factorial(2) = 6*5*4*3*2*factorial(1) = 6*5*4*3*2*1

Poiché abbiamo definito fattoriale (1) = 1, si raggiunge il "bottom".

In generale, le procedure ricorsive hanno due parti:

1) La parte ricorsiva, che definisce alcune procedure in termini di nuovi ingressi in combinazione con quello che hai "già fatto" tramite la stessa procedura. (ie factorial(n) = n*factorial(n-1))

2) una parte di base, il che fa in modo che il processo non si ripete per sempre dandogli qualche punto di partenza (es factorial(1) = 1)

Può essere un po 'di confusione per ottenere la testa intorno in un primo momento, ma basta guardare un po' di esempi e dovrebbe venire tutti insieme. Se si desidera una comprensione molto più profonda del concetto, studiare induzione matematica. Inoltre, essere consapevoli del fatto che alcune lingue ottimizzare per le chiamate ricorsive, mentre altri non lo fanno. E 'abbastanza facile fare funzioni ricorsive follemente lento se non stai attento, ma ci sono anche le tecniche per renderli performante nella maggior parte dei casi.

Spero che questo ti aiuti...

Risposto il 04/05/2010 a 14:30
fonte dall'utente

voti
4

Mi piace questa definizione:
In ricorsione, una routine risolve una piccola parte di un problema stesso, divide il problema in piccoli pezzi, e quindi si definisce per risolvere ciascuno dei pezzi più piccoli.

Mi piace anche Steve McConnells discussione della ricorsione in codice completo in cui egli critica gli esempi utilizzati nei libri di Informatica su ricorsione.

Non utilizzare la ricorsione per fattoriali o numeri di Fibonacci

Un problema con i libri di testo del computer-science è che essi presentano esempi sciocchi di ricorsione. Gli esempi tipici sono calcolare un fattoriale o calcolo di una sequenza di Fibonacci. La ricorsione è uno strumento potente, ed è davvero stupido da utilizzare in uno di questi casi. Se un programmatore che ha lavorato per me utilizzato ricorsione per calcolare un fattoriale, mi piacerebbe assumere qualcun altro.

Ho pensato che questo era un punto molto interessante per sollevare e può essere un motivo per cui la ricorsione è spesso frainteso.

EDIT: Questo non era una frecciata alla risposta di Dav - non avevo visto che la risposta quando ho postato questo

Risposto il 04/05/2010 a 12:29
fonte dall'utente

voti
3

Un esempio: Una definizione ricorsiva di una scala è: Una scala consiste di: - un unico passaggio e una scala (ricorsione) - o una sola fase (terminazione)

Risposto il 04/05/2010 a 14:34
fonte dall'utente

voti
3

Beh, questa è una definizione abbastanza decente che avete. E Wikipedia ha una buona definizione troppo. Quindi io aggiungo un'altra (probabilmente peggiore) definizione per voi.

Quando le persone si riferiscono a "ricorsione", sono di solito parlando di una funzione che hanno scritto che si chiama ripetutamente fino a quando non viene fatto con il suo lavoro. Ricorsione può essere utile quando si attraversa gerarchie in strutture di dati.

Risposto il 04/05/2010 a 12:27
fonte dall'utente

voti
3

Per recurse su un problema risolto: non fare nulla, il gioco è fatto.
Per recurse su un problema aperto: fare il passo successivo, quindi recurse sul resto.

Risposto il 06/08/2008 a 04:32
fonte dall'utente

voti
2

Questa è una vecchia questione, ma voglio aggiungere una risposta dal punto di vista logistico (cioè non dal punto di vista algoritmo di correttezza o un punto di vista delle prestazioni).

Io uso di Java per il lavoro, e Java non supporta la funzione annidata. In quanto tale, se voglio fare la ricorsione, potrei avere definire una funzione esterna (che esiste solo perché il mio codice di urti contro il dominio burocratico di Java), o potrei avere il refactoring del codice del tutto (che io odio fare).

Così, spesso evito ricorsione, e il funzionamento uso pila invece, perché ricorsione stessa è essenzialmente un'operazione pila.

Risposto il 30/08/2014 a 11:09
fonte dall'utente

voti
2

Una funzione ricorsiva è una funzione che contiene una chiamata a se stesso. Una struct ricorsiva è una struttura che contenga un'istanza di se stesso. È possibile combinare le due cose come classe ricorsiva. La parte fondamentale di un elemento ricorsivo è che contiene un'istanza / richiamo della stessa.

Consideriamo due specchi uno di fronte all'altro. Abbiamo visto l'effetto infinito accurato fanno. Ogni riflessione è un esempio di uno specchio, che è contenuta all'interno di un altro esempio di uno specchio, ecc Lo specchio contenente un riflesso di sé ricorsione.

Un albero binario di ricerca è un buon esempio di programmazione della ricorsione. La struttura è ricorsiva con ciascun Nodo contenente 2 istanze di un nodo. Funzioni di lavorare su un albero binario di ricerca sono anche ricorsivo.

Risposto il 04/05/2010 a 17:46
fonte dall'utente

voti
2

In parole povere: Si supponga che si può fare 3 cose:

  1. Prendere una mela
  2. Annotare tacche
  3. Conte tacche

Hai un sacco di mele di fronte a voi su un tavolo e si desidera sapere quante mele ci sono.

start
  Is the table empty?
  yes: Count the tally marks and cheer like it's your birthday!
  no:  Take 1 apple and put it aside
       Write down a tally mark
       goto start

Il processo di ripetere la stessa cosa fino a che si è fatto è chiamato ricorsione.

Spero che questo è il "inglese semplice" risposta che state cercando!

Risposto il 04/05/2010 a 14:09
fonte dall'utente

voti
1

La definizione più semplice della ricorsione è "autoreferenzialità". Una funzione che si riferisce a se stessa, ossia definisce sé è ricorsivo. La cosa più importante da tenere a mente, è che una funzione ricorsiva deve avere un "caso base", cioè una condizione che non se vero induce a chiamarsi, e quindi terminare la ricorsione. In caso contrario si avrà una ricorsione infinita:

ricorsione http://cart.kolix.de/wp-content/uploads/2009/12/infinite-recursion.jpg

Risposto il 04/05/2010 a 17:10
fonte dall'utente

voti
1

Ricorsione è quando si dispone di un'operazione che si utilizza. Probabilmente avrà un punto di sosta, altrimenti sarebbe andare avanti per sempre.

Diciamo che si desidera cercare una parola nel dizionario. Hai un'operazione denominata "look-up" a vostra disposizione.

Il tuo amico dice: "Potrei davvero cucchiaio un po 'di budino in questo momento!" Tu non sai quello che intende, in modo da guardare in alto "cucchiaio" nel dizionario e legge qualcosa di simile a questo:

Spoon: sostantivo - un utensile con una pallina rotonda alla fine. Spoon: verb - per usare un cucchiaio su qualcosa di Spoon: verbo - coccolare da vicino da dietro

Ora, essendo che non siete veramente bene con l'inglese, questo si punta nella direzione giusta, ma è necessario ulteriori informazioni. Quindi si seleziona "utensile" e "coccole" per guardare in alto per qualche informazione in più.

Cuddle: verbo - di coccole Utensile: sostantivo - uno strumento, spesso un utensile mangiare

Hey! Sai cosa coccole è, e non ha nulla a che fare con budino. Sapete anche che budino è qualcosa che si mangia, quindi ha senso ora. Il tuo amico deve desiderare di mangiare budino con un cucchiaio.

Ok, ok, questo è stato un esempio molto zoppo, ma illustra (forse poco) le due parti principali della ricorsione. 1) Si utilizza. In questo esempio, non si è davvero preso una parola per significato fino a comprendere, e che potrebbe significare cercare più parole. Questo ci porta al punto due, 2) Si ferma da qualche parte. Deve avere un qualche tipo di caso-base. In caso contrario, si era appena finisce per guardare ogni parola nel dizionario, che probabilmente non è troppo utile. La nostra base-caso era che avete ottenuto informazioni sufficienti per stabilire una connessione tra ciò che in precedenza ha fatto e non ha capito.

L'esempio tradizionale che è dato è fattoriale, dove 5 fattoriale è 1 * 2 * 3 * 4 * 5 (che è 120). Il caso base sarebbe 0 (o 1, a seconda). Quindi, per ogni numero intero n, fate quanto segue

è n uguale a 0? ritorno 1 altrimenti, ritornare n * (fattoriale di n-1)

cerchiamo di fare questo con l'esempio di 4 (che sappiamo in anticipo è di 1 * 2 * 3 * 4 = 24).

fattoriale di 4 ... è 0? no, quindi deve essere 4 * fattoriale di 3, ma ciò che è fattoriale di 3? è 3 * fattoriale di 2 fattoriale di 2 è 2 * fattoriale di 1 fattoriale di 1 è 1 * fattoriale di 0 e sappiamo fattoriale di 0! :-D è 1, che è il fattoriale definizione di 1 è 1 * fattoriale di 0, che era 1 ... così 1 * 1 = 1 fattoriale di 2 è 2 * fattoriale di 1, che era 1 ... così 2 * 1 = 2 fattoriale di 3 è 3 * fattoriale di 2, che era 2 ... così 3 * 2 = 6 fattoriale di 4 (finalmente !!) è 4 * fattoriale di 3, che era 6 ... 4 * 6 è 24

Fattoriale è un semplice caso di "caso base, e si utilizza".

Ora, notiamo stavamo ancora lavorando fattoriale del 4 per tutta la strada verso il basso ... Se volessimo fattoriale di 100, avremmo dovuto andare fino in fondo a 0 ... che potrebbe avere un sacco di spese generali ad esso. Allo stesso modo, se troviamo una parola oscura per cercare nel dizionario, potrebbe richiedere guardando altre parole e di scansione per il contesto indizi fino a trovare un collegamento che siamo a conoscenza. metodi ricorsivi può richiedere molto tempo per farsi strada attraverso. Tuttavia, quando sono utilizzati in modo corretto, e compresi, possono fare il lavoro complicato sorprendentemente semplice.

Risposto il 04/05/2010 a 17:04
fonte dall'utente

voti
1

Un gran numero di problemi possono essere pensati in due tipi di pezzi:

  1. casi di base, che sono cose elementari che si può risolvere da solo a guardarli, e
  2. casi ricorsivi, che costruiscono un problema più grande di pezzi più piccoli (elementari o altrimenti).

Così che cosa è una funzione ricorsiva? Beh, è ​​lì che si dispone di una funzione che è definita in termini di per sé, direttamente o indirettamente. OK, che suona ridicolo fino a quando si rende conto che è ragionevole per i problemi del tipo sopra descritto: a risolvere i casi di base direttamente e affrontare i casi ricorsive utilizzando chiamate ricorsive per risolvere i pezzi più piccoli del problema incorporato all'interno.

L'esempio classico di veramente in cui è necessario ricorsione (o qualcosa che odora di molto simile) è quando hai a che fare con un albero. Le foglie dell'albero sono il caso base, ed i rami sono caso ricorsivo. (In pseudo-C.)

struct Tree {
    int leaf;
    Tree *leftBranch;
    Tree *rightBranch;
};

Il modo più semplice di stampare questo fuori in modo è quello di utilizzare la ricorsione:

function printTreeInOrder(Tree *tree) {
    if (tree->leftBranch) {
        printTreeInOrder(tree->leftBranch);
    }
    print(tree->leaf);
    if (tree->rightBranch) {
        printTreeInOrder(tree->rightBranch);
    }
}

E 'morto facile vedere che che sta andando a lavorare, visto che è cristallina. (L'equivalente non ricorsiva è un bel po 'più complesso, che richiede una struttura di stack internamente per gestire l'elenco delle cose da elaborare.) Beh, partendo dal presupposto che nessuno ha fatto un collegamento circolare naturalmente.

Matematicamente, il trucco per mostrare che la ricorsione si doma è quello di concentrarsi sulla ricerca di una metrica per la dimensione degli argomenti. Per il nostro esempio albero, la metrica più semplice è la profondità massima dell'albero sotto il nodo attuale. A foglie, è pari a zero. Nel corso di una filiale fogli con solo al di sotto di esso, è uno, ecc Poi si può semplicemente dimostrare che ci sia rigorosamente ordinato sequenza delle dimensioni degli argomenti che la funzione viene richiamata su al fine di elaborare l'albero; Gli argomenti per chiamate ricorsive sono sempre "minore" nel senso della metrica che l'argomento alla chiamata generale. Con una metrica cardinale strettamente decrescente, si sta ordinati.

E 'anche possibile avere una ricorsione infinita. Questo è disordinato e in molte lingue non funzionerà perché lo stack fa saltare in aria. (Dove funziona, il motore lingua deve essere la determinazione che la funzione in qualche modo non ritorna ed è in grado quindi di ottimizzare il via la tenuta della pila di roba Tricky in generale;. Coda ricorsione è solo il modo più banale di fare questo .)

Risposto il 04/05/2010 a 16:29
fonte dall'utente

voti
1

Ricorsione è tecnica di definire una funzione, un set o un algoritmo in termini di se stessa.

Per esempio

n! = n(n-1)(n-2)(n-3)...........*3*2*1

Ora può essere definito in modo ricorsivo come: -

n! = n(n-1)!   for n>=1

In termini di programmazione, quando una funzione o metodo stesso chiamate ripetutamente, fino a qualche condizione specifica viene soddisfatta, questo processo è chiamato ricorsione. Ma ci deve essere una condizione di terminazione e la funzione o metodo non deve entrare in un ciclo infinito.

Risposto il 04/05/2010 a 16:22
fonte dall'utente

voti
1

Ricorsione nelle computing è una tecnica usata per calcolare un effetto risultato o laterale seguendo il rendimento normale da una singola funzione (metodo, procedura o blocco) invocazione.

La funzione ricorsiva, per definizione, deve avere la capacità di invocare stessa direttamente o indirettamente (tramite altre funzioni) secondo una condizione di uscita o condizioni non siano soddisfatte. Se una condizione di uscita viene soddisfatta l'invocazione particolari torna ad esso del chiamante. Questo continua fino a quando la chiamata iniziale viene restituito dal, momento in cui l'effetto risultato o lato desiderato sarà disponibile.

A titolo di esempio, ecco una funzione per eseguire l'algoritmo Quicksort a Scala ( copiato dalla voce di Wikipedia per Scala )

def qsort: List[Int] => List[Int] = {
  case Nil => Nil
  case pivot :: tail =>
    val (smaller, rest) = tail.partition(_ < pivot)
    qsort(smaller) ::: pivot :: qsort(rest)
}

In questo caso la condizione di uscita è un elenco vuoto.

Risposto il 04/05/2010 a 15:14
fonte dall'utente

voti
1

funzione stessa chiamata o utilizzare una propria definizione.

Risposto il 04/05/2010 a 14:59
fonte dall'utente

voti
1

Qualsiasi algoritmo presenta strutturale ricorsione su un tipo di dati se fondamentalmente costituito da un interruttore-dichiarazione con un caso per ciascun caso del tipo di dati.

ad esempio, quando si lavora su un tipo

  tree = null 
       | leaf(value:integer) 
       | node(left: tree, right:tree)

un algoritmo ricorsivo strutturale avrebbe la forma

 function computeSomething(x : tree) =
   if x is null: base case
   if x is leaf: do something with x.value
   if x is node: do something with x.left,
                 do something with x.right,
                 combine the results

questo è davvero il modo più ovvio per scrivere qualsiasi algoritmo che lavora su una struttura di dati.

Ora, quando si guardano le interi (o meglio, i numeri naturali), come definiti utilizzando gli assiomi di Peano

 integer = 0 | succ(integer)

si vede che un algoritmo ricorsivo strutturale su interi si presenta così

 function computeSomething(x : integer) =
   if x is 0 : base case
   if x is succ(prev) : do something with prev

la funzione fattoriale troppo-noto riguarda l'esempio più banale di questa forma.

Risposto il 04/05/2010 a 14:53
fonte dall'utente

voti
1

Hey, mi dispiace se la mia opinione è d'accordo con qualcuno, sto solo cercando di spiegare la ricorsione in parole povere.

si supponga di avere tre manager - Jack, John e Morgan. Jack gestisce 2 programmatori, John - 3, e Morgan - 5. che si sta per dare ogni manager di 300 $ e si desidera sapere che cosa sarebbe costato. La risposta è ovvia - ma cosa succede se due di dipendenti Morgan-s sono anche i manager?

Ecco che arriva la ricorsione. ci si trova nella parte superiore della gerarchia. il costo estivo è 0 $. si inizia con Jack, quindi verificare se ha qualche manager come dipendenti. se si trova qualcuno di loro sono, controllare se hanno i manager come dipendenti e così via. Aggiungere 300 $ al costo estivo ogni volta che si trova un manager. quando si è finito con Jack, andare a John, i suoi dipendenti e poi a Morgan.

Non saprai mai, quanto i cicli andrai prima di ottenere una risposta, anche se non si sa quanti manager avete e quante Budget si può spendere.

La ricorsione è un albero, con rispettivamente rami e foglie, chiamati genitori e figli. Quando si utilizza un algoritmo di ricorsione, è più o meno consapevolmente stanno costruendo un albero dai dati.

Risposto il 04/05/2010 a 13:50
fonte dall'utente

voti
1

In parole povere, la ricorsione significa ripetere ancora e ancora someting.

In un esempio di programmazione è di chiamare la funzione in sé.

Guardate il seguente esempio di calcolo fattoriale di un numero:

public int fact(int n)
{
    if (n==0) return 1;
    else return n*fact(n-1)
}
Risposto il 04/05/2010 a 13:48
fonte dall'utente

voti
1

Ricorsione è il processo in cui una chiamata di metodo iself poter eseguire una determinata operazione. Riduce redundency di codice. Maggior parte delle funzioni recurssive o metodi devono avere un condifiton per rompere il recussive chiamare ossia impedire che chiamarsi se la condizione è soddisfatta - ciò impedisce la creazione di un ciclo infinito. Non tutte le funzioni sono adatti per essere utilizzati in modo ricorsivo.

Risposto il 04/05/2010 a 13:42
fonte dall'utente

voti
1

è un modo per fare le cose più e più volte a tempo indeterminato tale che ogni opzione viene utilizzata.

per esempio, se si voleva ottenere tutti i link in una pagina HTML che si vuole avere ricorsioni perché quando si ottiene tutti i link a pagina 1 si vuole ottenere tutti i link su ciascuno dei collegamenti presenti sulla prima pagina. poi per ogni link a un newpage si vuole quei link e così via ... in altre parole, si tratta di una funzione che si chiama da dentro se stesso.

Quando si esegue questa operazione è necessario un modo per sapere quando fermarsi, altrimenti vi troverete in un ciclo infinito in modo da aggiungere un parametro intero per la funzione di tenere traccia del numero di cicli.

in C # si avrà qualcosa di simile a questo:

private void findlinks(string URL, int reccursiveCycleNumb)    {
   if (reccursiveCycleNumb == 0)
        {
            return;
        }

        //recursive action here
        foreach (LinkItem i in LinkFinder.Find(URL))
        {
            //see what links are being caught...
            lblResults.Text += i.Href + "<BR>";

            findlinks(i.Href, reccursiveCycleNumb - 1);
        }

        reccursiveCycleNumb -= reccursiveCycleNumb;
}
Risposto il 04/05/2010 a 13:02
fonte dall'utente

voti
1

"Se ho un martello, far sembrare tutto come un chiodo."

La ricorsione è una strategia di problem-solving per enormi problemi, in cui ad ogni passo solo, "chiavi 2 piccole cose in una cosa più grande", ogni volta con lo stesso martello.

Esempio

Supponiamo che la vostra scrivania è coperta con un pasticcio disorganizzato di 1024 carte. Come si fa a fare un ordinato, pulito pila di carte dal caos, utilizzando la ricorsione?

  1. Divide: Stendere tutti i fogli fuori, in modo da avere un solo foglio in ogni "stack".
  2. Conquistare:
    1. Andare in giro, mettendo ogni foglio in cima a un altro foglio. Ora avete pile di 2.
    2. Andare in giro, mettendo ogni 2-stack su un altro 2-stack. Ora avete pile di 4.
    3. Andare in giro, mettendo ogni 4-stack su un altro 4-stack. Ora avete pile di 8.
    4. ... ancora e ancora ...
    5. Si dispone ora di una pila enorme di 1024 fogli!

Si noti che questo è abbastanza intuitivo, oltre a contare tutto ciò (che non è strettamente necessario). Si potrebbe non andare fino in fondo a pile 1 fogli, in realtà, ma si potrebbe e sarebbe ancora funzionare. La parte importante è il martello: con le braccia, si può sempre mettere una pila sopra l'altro per fare uno stack più grande, e non importa (a ragione) quanto grande sia stack è.

Risposto il 04/05/2010 a 12:54
fonte dall'utente

voti
1

Ricorsione come si applica alla programmazione è fondamentalmente chiamando una funzione dall'interno propria definizione (proprio interno), con diversi parametri in modo da realizzare un compito.

Risposto il 04/05/2010 a 12:25
fonte dall'utente

voti
1

Si desidera utilizzare in qualsiasi momento si dispone di una struttura ad albero. E 'molto utile nella lettura XML.

Risposto il 21/08/2008 a 14:18
fonte dall'utente

voti
1

Mario, non capisco il motivo per cui si è utilizzato ricorsione per questo esempio .. Perché non semplicemente ciclo attraverso ogni voce? Qualcosa come questo:

String ArrangeString(TStringList* items, String separator)
{
    String result = items->Strings[0];

    for (int position=1; position < items->count; position++) {
        result += separator + items->Strings[position];
    }

    return result;
}

Il metodo di cui sopra sarebbe più veloce, ed è più semplice. Non c'è alcun bisogno di usare la ricorsione al posto di un semplice ciclo. Credo che questo tipo di esempi è il motivo per cui la ricorsione ottiene un rap cattivo. Anche l'esempio funzione fattoriale canonica è meglio implementato con un ciclo.

Risposto il 06/08/2008 a 04:19
fonte dall'utente

voti
0

In realtà la soluzione migliore per i ricorsiva fattoriale dovrebbe essere:

int factorial_accumulate(int n, int accum) {
    return (n < 2 ? accum : factorial_accumulate(n - 1, n * accum));
}

int factorial(int n) {
    return factorial_accumulate(n, 1);
}

Poiché questa versione è di coda ricorsiva

Risposto il 21/08/2008 a 14:39
fonte dall'utente

voti
0

Io uso ricorsione. Che cosa ha a che fare con l'avere una laurea CS ... (che non lo faccio, tra l'altro)

Gli usi comuni che ho trovato:

  1. Sitemap - recurse attraverso file system a partire da radice del documento
  2. ragni - che strisciano attraverso un sito web per trovare l'indirizzo e-mail, link, ecc
  3. ?
Risposto il 06/08/2008 a 04:13
fonte dall'utente

voti
0

Ho creato una funzione ricorsiva per concatenare una lista di stringhe con un separatore tra di loro. Io lo uso principalmente per creare espressioni SQL, facendo passare un elenco di campi come il ' voci ' e una ' virgola + spazio ' come separatore. Ecco la funzione (Esso utilizza alcuni tipi di dati nativo Borland Builder, ma può essere adattato a qualsiasi altro ambiente):

String ArrangeString(TStringList* items, int position, String separator)
{
  String result;

  result = items->Strings[position];

  if (position <= items->Count)
    result += separator + ArrangeString(items, position + 1, separator);

  return result;
}

Io lo chiamo in questo modo:

String columnsList;
columnsList = ArrangeString(columns, 0, ", ");

Immaginate di avere una matrice 'denominata campi ' con questi dati al suo interno: ' albumName ', ' ReleaseDate ', ' labelId '. Poi si chiama la funzione:

ArrangeString(fields, 0, ", ");

Poiché la funzione inizia a funzionare, il 'variabile risultato ' riceve il valore della posizione 0 della matrice, che è ' albumName '.

Poi controlla se la posizione è trattare con è l'ultimo. Poiché non è, allora concatena il risultato con il separatore e il risultato di una funzione che, oh Dio, è la stessa funzione. Ma questa volta, il check-out, si chiama se stesso aggiungendo 1 alla posizione.

ArrangeString(fields, 1, ", ");

Si continua a ripetere, la creazione di una pila LIFO, fino a raggiungere un punto in cui la posizione in corso di esame è l'ultimo, quindi la funzione restituisce solo la voce su quella posizione nella lista, non concatenare più. Poi il palo è concatenato all'indietro.

Fatto? Se non lo fai, ho un altro modo di spiegarlo. : O)

Risposto il 06/08/2008 a 04:00
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more