Ce este recursivitatea și când ar trebui să-l folosesc?

voturi
121

Unul dintre subiectele care pare să vină în mod regulat pe listele de discuții și discuții on-line este meritele (sau lipsa acestora) de a face un calculator Science. Un argument care pare să vină de timp și din nou pentru partid negativ este că acestea au fost de codificare pentru un număr de ani și nu au mai folosit recursivitate.

Deci, întrebarea este:

  1. Ce este recursivitatea?
  2. Când mi-ar folosi recursivitate?
  3. De ce oamenii nu folosesc recursivitate?
Întrebat 06/08/2008 la 03:29
sursa de către utilizator
În alte limbi...                            


40 răspunsuri

voturi
86

Există o serie de explicații bune ale recursie în acest thread, acest răspuns este de ce nu ar trebui să - l utilizați în cele mai multe limbi. * In majoritatea majore implementari de limbaj imperativ ( de exemplu , fiecare punere în aplicare majoră a C, C ++, Basic, Python , Ruby, Java și C #) iterație este mult preferabilă recursivitate.

Pentru a vedea de ce, plimbare prin pașii pe care limbile de mai sus folosesc pentru a apela o funcție:

  1. spațiu este sculptat pe stivă pentru argumentele functiei si variabile locale
  2. argumentele functiei sunt copiate in acest nou spatiu
  3. de control sare la funcția
  4. ruleaza de cod functiei
  5. Rezultatul funcției este copiată într-o valoare de retur
  6. stiva este bobinat invers în poziția sa anterioară
  7. de control sare înapoi în cazul în care a fost apelată funcția

Făcând toți acești pași are nevoie de timp, de obicei , un pic mai mult decât este nevoie pentru a itera printr - o buclă. Cu toate acestea, problema reală este în pasul # 1. Atunci când mai multe programe începe, ele alocă o singură bucată de memorie pentru stiva lor, iar atunci când a alerga afară de faptul că memoria ( de multe ori, dar nu întotdeauna din cauza recursivitate), programul se blochează din cauza unei overflow stivă .

Deci, în aceste limbi recursiune este mai lent și te face vulnerabil la crashing. Există încă unele argumente pentru utilizarea acestuia, totuși. În general, codul scris recursiv este mai scurt si un pic mai elegant, o dată ce știi cum să-l citească.

Există o tehnică pe care implementatorii de limbaj poate utiliza numit optimizare apel coada , care poate elimina unele clase de depășire de stivă. Succint: dacă expresia unei funcții de întoarcere este pur și simplu rezultatul unui apel de funcție, atunci nu este necesar să adăugați un nou nivel pe stivă, aveți posibilitatea să reutilizați cel curent pentru funcția fiind numit. Din păcate, puține imperative de limbaj implementari de optimizare au coada-apel încorporat.

* Îmi place de recurență. Limba mea preferată static nu utilizează bucle , la toate, recurențe este singura modalitate de a face ceva în mod repetat. Pur și simplu nu cred că recursivitatea este , în general , o idee bună de limbi care nu sunt reglate pentru ea.

** Prin modul în care Mario, numele tipic pentru funcția dumneavoastră ArrangeString este „se alăture“, și aș fi surprins dacă limba dvs. de alegere nu are deja o punere în aplicare a acesteia.

Publicat 06/08/2008 la 06:09
sursa de către utilizator

voturi
63

exemplu engleză simplă recursivitatii.

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.
Publicat 04/05/2010 la 17:38
sursa de către utilizator

voturi
49

În sensul cel mai de bază informatică, recurențe este o funcție care se numește. Să presupunem că aveți o structură de listă legată:

struct Node {
    Node* next;
};

Și vrei să afli cât de mult timp o listă legată este de a putea face acest lucru cu recursivitate:

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

(Acest lucru ar putea, desigur, să fie făcut cu o buclă de asemenea, dar este util ca o ilustrare a conceptului)

Publicat 04/05/2010 la 12:25
sursa de către utilizator

voturi
46

Ori de câte ori o funcție în sine numește, creând o buclă, atunci asta e recursivitate. Ca și în orice există utilizări bune și rele utilizări pentru recursivitate.

Cel mai simplu exemplu este recursivitate coada în cazul în care chiar ultima linie a funcției este o chemare la sine:

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

Cu toate acestea, acest lucru este un exemplu de lame, aproape inutil, deoarece acesta poate fi ușor înlocuită cu iterație mai eficientă. La urma urmelor, recurențe suferă de regie funcție de apel, care, în exemplul de mai sus ar putea fi substanțial în comparație cu funcționarea în interiorul funcției în sine.

Deci , întregul motiv pentru a face recursivitate , mai degrabă decât de repetare ar trebui să fie pentru a profita de stiva de apel de a face unele lucruri inteligent. De exemplu, dacă apelați o funcție de mai multe ori cu diferiți parametri în interiorul aceeași buclă , atunci este o modalitate de a realiza ramificare . Un exemplu clasic este triunghiul Sierpinski .

introduceți descrierea imaginii aici

Puteți desena unul dintre cei foarte simplu, cu recursivitate, în cazul în care ramurile stiva de apel în 3 direcții:

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);
    }
}

Dacă încercați să faceți același lucru cu iterație cred că vei găsi este nevoie de o mai mult cod pentru a realiza.

Alte cazuri comune de utilizare ar putea include ierarhii traversați, de exemplu, site-ul crawlerele, comparații de director etc.

Concluzie

În termeni practici, recurențe face cel mai mult sens ori de câte ori aveți nevoie de ramificare iterativ.

Publicat 04/05/2010 la 14:33
sursa de către utilizator

voturi
28

Recursivitatea este o metodă de rezolvare a problemelor bazate pe mentalitatea divide și cucerește. Ideea de bază este că luați problema originală și împărțiți-l în mai mici cazuri (mai ușor de rezolvat) de sine, să rezolve aceste cazuri mai mici (de obicei, folosind din nou același algoritm) și apoi le reasambla în soluția finală.

Exemplul canonic este o rutină pentru a genera factorial n. Factorialul n se calculează prin înmulțirea toate numerele între 1 și n. O soluție iterativ în C # arată astfel:

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

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

  return fact;
}

Nu e nimic surprinzător despre soluția iterativ și ar trebui să aibă sens pentru oricine familiarizat cu C #.

Soluția recursivă este găsită prin recunoașterea faptului că nth factorial este n * Fapt (n-1). Sau, un alt mod, dacă știi ce un anumit număr factorial este puteți calcula următoarea. Aici este soluția recursiv în C #:

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

  return n * FactRec( n - 1 );
}

Prima parte a acestei funcții este cunoscută ca o cauza de bază (sau , uneori , Garda Clauza) și este ceea ce împiedică algoritmul de rulare pentru totdeauna. Doar returnează valoarea 1 de fiecare dată când funcția este numit cu o valoare de 1 sau mai puțin. A doua parte este mult mai interesant și este cunoscut sub numele de Pasul recursive . Aici noi numim aceeași metodă cu un parametru ușor modificat (l - am decrement de 1) și apoi se înmulțește rezultatul cu copia noastră de n.

Atunci când prima întâlnit acest lucru poate fi un fel de confuz, astfel că este instructiv să examineze modul în care funcționează atunci când rula. Imaginați-vă că noi numim FactRec (5). Noi intra în rutină, nu sunt preluate de cazul de bază și așa am ajunge ca acest lucru:

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

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

Dacă vom re-intra metoda cu parametrul 4 suntem din nou, nu a fost oprit de clauza de protecție și așa am ajunge la:

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

Dacă înlocuim această valoare returnată în valoarea returnată de mai sus obținem

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

Acest lucru ar trebui să vă dau un indiciu cu privire la modul în care se ajunge la soluția finală așa că vom urmări rapid și arată fiecare pas pe drum în jos:

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

Această substituție finală se întâmplă atunci când cazul de bază este declanșată. În acest moment, avem o formulă algrebraic simplu pentru a rezolva ceea ce echivaleaza direct la definiția factorial, în primul rând.

Este instructiv să rețineți că fiecare apel în rezultatele metodei fie într-un caz de bază fiind declanșată sau un apel la aceeași metodă în cazul în care parametrii sunt mai aproape de un caz de bază (adesea numit un apel recursiv). În cazul în care acest lucru nu este cazul, atunci metoda va rula pentru totdeauna.

Publicat 06/08/2008 la 03:54
sursa de către utilizator

voturi
12

Recursivitatea este rezolvarea unei probleme cu o funcție care se numește. Un bun exemplu în acest sens este o funcție factorial. Factorial este o problemă de matematică, unde factorial de 5, de exemplu, este de 4 * 5 * 3 * 2 * 1. Această funcție rezolvă acest lucru în C # pentru întregi pozitivi (nu a fost testat - poate exista un bug).

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

    return n * Factorial(n - 1);
}
Publicat 04/05/2010 la 12:29
sursa de către utilizator

voturi
9

Luați în considerare o problemă veche, bine cunoscut :

În matematica, cel mai mare divizor comun (GCD) ... din două sau mai multe numere întregi de zero, este cel mai mare număr întreg pozitiv care împarte numerele fără rest.

Definirea cmmdc este extrem de simplu:

definiţia GCD

unde mod este operatorul modulo (adică, restul după divizare întreg).

În limba engleză, această definiție spune cel mai mare divizor comun al oricărui număr și zero , este faptul că numărul, iar cel mai mare divizor comun a două numere m și n este cel mai mare divizor comun al n , iar restul după împărțirea m de n .

Dacă doriți să știți de ce funcționează, a se vedea articolul Wikipedia pe algoritmul lui Euclid .

Hai calcula cmmdc (10, 8), ca un exemplu. Fiecare pas este egal cu cel pe care tocmai înainte de a fi:

  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

În prima etapă, 8 nu este egal cu zero, deci se aplică a doua parte a definiției. 10 Mod 8 = 2, deoarece 8 merge în 10 o dată cu un rest de 2. La pasul 3, partea a doua se aplică din nou, dar de data aceasta 8 mod 2 = 0, deoarece 2 împărțiri 8 fără rest. La pasul 5, al doilea argument este 0, astfel încât răspunsul este 2.

Ai observat că GCD apare pe ambele părți stânga și dreapta semnului egal? Un matematician ar spune această definiție este recursiv , deoarece expresia te definirea reapar in definiția sa.

definiții recursive tind să fie elegant. De exemplu, o definiție recursivă pentru suma unei liste este

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

în cazul în care headeste primul element dintr - o listă și taileste restul listei. Rețineți că se sumrepetă în interiorul definiția sa la sfârșitul anului .

Poate ați prefera valoarea maximă dintr-o listă în schimb:

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

S-ar putea defini multiplicarea numere întregi ne-negative recursiv să-l transforme într-o serie de completări:

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

În cazul în care pic despre transformarea multiplicarea într-o serie de adăugiri nu are nici un sens, încercați extinderea câteva exemple simple pentru a vedea cum funcționează.

Merge un fel are o definiție minunat recursiv:

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

Definițiile recursive sunt peste tot în jurul dacă știi ce să caute. Observați cum toate aceste definiții au cazuri de bază foarte simple, de exemplu , GCD (m, 0) = m. Cazurile recursive whittle departe la problema pentru a ajunge în jos răspunsurile ușor.

Cu această înțelegere, puteți aprecia acum alți algoritmi din articolul Wikipedia pe recursivitate !

Publicat 04/05/2010 la 14:58
sursa de către utilizator

voturi
9

Recursivitatea se referă la o metodă care rezolvă o problemă prin rezolvarea o versiune mai mică a problemei și apoi folosind acest rezultat, plus un alt calcul pentru a formula răspunsul la problema inițială. De multe ori, în procesul de soluționare versiunea mai mică, metoda va rezolva o versiune încă mai mică a problemei, și așa mai departe, până când ajunge la un „caz de bază“, care este trivial pentru a rezolva.

De exemplu, pentru a calcula un factorial pentru numărul X, o poate reprezenta ca X times the factorial of X-1. Astfel, metoda „recurses“ pentru a găsi factorialul X-1, apoi inmulteste orice ar fi luat de Xa da un răspuns final. Desigur, pentru a găsi factorialul X-1, acesta va calcula mai întâi factorialul X-2, și așa mai departe. Cazul de bază ar fi atunci când Xeste 0 sau 1, în cazul în care știe să se întoarcă de 1atunci 0! = 1! = 1.

Publicat 04/05/2010 la 12:26
sursa de către utilizator

voturi
9
  1. O funcție care se numește
  2. Atunci când o funcție poate fi (ușor) descompus într-o operație simplă, plus aceeași funcție pe unele porțiune mai mică a problemei. Aș spune, mai degrabă, că acest lucru el un candidat bun pentru recursivitate face.
  3. Ei fac!

Exemplul canonic este factorial, care arata ca:

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

  return a*fact(a-1);
}

În general, recurențe nu este neapărat rapid (deasupra capului de apel funcția tinde să fie mai mare, deoarece funcțiile recursive tind să fie mici, a se vedea mai sus) și pot suferi de unele probleme (stiva de preaplin cineva?). Unii spun că ele tind să fie greu pentru a obține „drept“ în cazurile în care nu triviale, dar eu nu cumpăr într-adevăr în asta. În unele situații, recurențe face cel mai mult sens și este modul cel mai elegant și clar pentru a scrie o anumită funcție. Trebuie remarcat faptul că unele limbi favorizează soluții recursive și le optimiza mult mai mult (LISP vine în minte).

Publicat 06/08/2008 la 03:35
sursa de către utilizator

voturi
7

O funcție recursiva este una care se numește. Cel mai frecvent motiv pentru care am găsit să-l utilizați traversează în o structură arborescentă. De exemplu, dacă am un TreeView cu casetele de selectare (cred că instalarea unui nou program „alegeți caracteristici vor fi instalate“ pagina), s-ar putea dori un buton „selectați toate“, care ar fi ceva de genul (pseudocod):

function cmdCheckAllClick {
    checkRecursively(TreeView1.RootNode);
}

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

Deci, puteți vedea că checkRecursively verifică mai întâi nodul pe care este trecut, apoi se solicită pentru fiecare dintre copii, care nod.

Ai nevoie să fie un pic mai atent cu recursivitate. Dacă obține într-o buclă recursiv infinit, veți obține o excepție de Stack Overflow :)

Nu pot gândi la un motiv pentru care oamenii nu ar trebui să-l folosească, atunci când este cazul. Este util în anumite circumstanțe, și nu în altele.

Cred că pentru că este o tehnică interesantă, unele codoare poate ajunge folosind mai des decât ar trebui, fără o justificare reală. Acest lucru a dat recursie un nume de rău, în unele cercuri.

Publicat 06/08/2008 la 03:44
sursa de către utilizator

voturi
5

Recursivitatea este o expresie cu referire în mod direct sau indirect, în sine.

Luați în considerare acronime recursive ca un exemplu simplu:

  • GNU standuri pentru GNU Nu -i Unix
  • PHP standuri pentru PHP: Hypertext Preprocessor
  • YAML standuri pentru YAML nu este Markup Language
  • VIN reprezintă vinul nu este un emulator
  • VISA standuri pentru Visa International Service Association

Mai multe exemple de pe Wikipedia

Publicat 04/05/2010 la 12:56
sursa de către utilizator

voturi
5

Iată un exemplu simplu: cât de multe elemente dintr-un set. (Există modalități mai bune de a conta lucruri, dar acest lucru este un frumos exemplu recursiv simplu.)

În primul rând, avem nevoie de două reguli:

  1. dacă setul este gol, numărul de elemente din setul este zero (duh!).
  2. în cazul în care setul nu este gol, numărul este un plus numărul de articole din set, după un singur element este eliminat.

Să presupunem că aveți un set de genul: [xxx]. Să numărăm câte elemente sunt.

  1. setul este [xxx], care nu este gol, așa că aplicăm regula 2. numărul de articole este un plus numărul de articole în [xx] (de exemplu, am eliminat un element).
  2. setul este [xx], așa că aplicăm regula 2 din nou: numărul unu + de articole în [x].
  3. setul este [x], care se potrivește în continuare regula 2: un număr + de articole în [].
  4. Acum setul este [], care se potrivește cu regula 1: numărul este zero!
  5. Acum, că știm răspunsul la pasul 4 (0), putem rezolva pasul 3 (1 + 0)
  6. De asemenea, acum că știm răspunsul la pasul 3 (1), putem rezolva etapa 2 (1 + 1)
  7. Și, în sfârșit acum că știm răspunsul în etapa 2 (2), putem rezolva etapa 1 (1 + 2) și pentru a obține numărul de elemente din [xxx], care este 3. Uraaa!

Putem reprezenta aceasta:

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

Atunci când se aplică o soluție recursiv, aveți de obicei cel puțin 2 reguli:

  • baza, caz simplu, care prevede ce se întâmplă atunci când au „folosit“ toate datele. Aceasta este, de obicei, o variantă a „dacă sunteți în afara datelor de proces, răspunsul este X“
  • regula recursiv, care prevede ce se întâmplă dacă aveți în continuare date. Acesta este de obicei un fel de regula care spune „face ceva pentru a face datele stabilite mai mici și aplicați din nou regulile pentru setul de date mai mici.“

Dacă traducem cele de mai sus la pseudocod, obținem:

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

Sunt multe exemple mai utile (traversează un copac, de exemplu) care sunt sigur ca alte persoane vor acoperi.

Publicat 06/08/2008 la 04:12
sursa de către utilizator

voturi
5

Recursivitatea funcționează cel mai bine cu ceea ce îmi place să numesc „probleme fractal“, în cazul în care ai de a face un lucru mare care a făcut din versiuni mai mici ale acestui lucru mare, fiecare dintre care este o versiune mai mică a mare lucru, și așa mai departe. Dacă aveți vreodată să traverseze sau căuta prin ceva de genul unui copac sau structuri identice imbricate, ai o problemă care ar putea fi un candidat bun pentru recursivitate.

Oamenii evita recursivitate pentru mai multe motive:

  1. Cei mai mulți oameni (inclusiv eu) tăiat dinții lor de programare pe programarea procedurală sau orientate obiect, spre deosebire de programarea funcțională. Pentru astfel de oameni, abordarea iterativă (de obicei, folosind bucle) se simte mai natural.

  2. Aceia dintre noi care taie dinții noștri de programare pe programarea procedurală sau orientate pe obiect au fost spus de multe ori, pentru a evita recursivitate, deoarece este predispus la erori.

  3. de multe ori ni se spune că recursivitate este lent. Apelarea și întoarcerea dintr-o rutină implică în mod repetat o mulțime de stivă împingere și popping, care este mai lent decât looping. Cred că unele limbi se ocupe de acest lucru mai bine decât altele, iar aceste limbi sunt cel mai probabil, cele în care paradigma dominantă este de natură procedurală sau orientat pe obiect.

  4. Pentru cel puțin o pereche de limbaje de programare am folosit, îmi amintesc recomandări de auz să nu folosească recursivitate în cazul în care devine dincolo de o anumită adâncime, deoarece stiva sa nu este atât de adânc.

Publicat 06/08/2008 la 04:12
sursa de către utilizator

voturi
4

1.) O metodă este recursiv în cazul în care se poate apela; fie direct:

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

sau indirect:

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

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

2.) Când se utilizează recursivitate

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.) Oamenii folosesc recursivitate numai atunci când este foarte complexă pentru a scrie cod iterativ. De exemplu, tehnici de copac, cum ar fi traversal precomandă, postordine se poate face atat iterativ si recursiv. Dar, de obicei, vom folosi recursiv din cauza simplității sale.

Publicat 11/03/2014 la 10:47
sursa de către utilizator

voturi
4

O declarație recursiva este una în care ați defini procesul de ceea ce să facă în continuare ca o combinație a intrărilor și ceea ce ați făcut deja.

De exemplu, să ia factorial:

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

Dar este ușor de văzut factorial (6) De asemenea, este:

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

Deci, în general:

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

Desigur, lucru complicat despre recursivitate este că, dacă doriți să definiți lucrurile în ceea ce privește ceea ce ați făcut deja, trebuie să existe un loc pentru a începe.

În acest exemplu, vom face doar un caz special prin definirea factorial (1) = 1.

Acum vom vedea de jos în sus:

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

Din moment ce am definit factorial (1) = 1, vom ajunge la "partea de jos".

În general vorbind, procedurile recursive au două părți:

1) Partea recursiv, care definește o anumită procedură în ceea ce privește noi intrări , combinate cu ceea ce ai „deja făcut“ prin aceeași procedură. ( de exemplu factorial(n) = n*factorial(n-1))

2) O parte de bază, ceea ce face sigur că procesul nu se repetă pentru totdeauna, dându - i un loc pentru a începe ( de exemplu factorial(1) = 1)

Acesta poate fi un pic confuz pentru a obține capul în jurul valorii de la început, dar doar uita-te la o grămadă de exemple și ar trebui să vină împreună. Dacă doriți o înțelegere mai profundă a conceptului, studiu de inducție matematică. De asemenea, să fie conștienți de faptul că anumite limbi pentru a optimiza apelurile recursive în timp ce altele nu. Este destul de ușor de a face funcții recursive incredibil de lent, dacă nu ești atent, dar există, de asemenea, tehnici de a le face performante în cele mai multe cazuri.

Sper că acest lucru vă ajută...

Publicat 04/05/2010 la 14:30
sursa de către utilizator

voturi
4

Îmi place această definiție:
În recursivitate, o rutină rezolvă o mică parte dintr - o problemă în sine, împarte problema în bucăți mai mici, iar apoi se solicită să rezolve fiecare dintre piesele mai mici.

Îmi place, de asemenea, discuția Steve McConnells recursivitatii în codul complet în cazul în care el critică exemplele folosite în cărți de informatică pe recursivitate.

Nu utilizați recursivitate pentru factorialele sau numere Fibonacci

O problemă cu manuale de calculator-știință este că acestea prezintă exemple stupide recursivitatii. Exemplele tipice sunt un calcul factorial sau calcularea unei secvențe Fibonacci. Recursivitatea este un instrument puternic, și este foarte prost să-l folosească în oricare dintre aceste cazuri. În cazul în care un programator care a lucrat pentru mine folosit recursivitate pentru a calcula un factorial, aș angaja pe altcineva.

Am crezut că acest lucru a fost un punct foarte interesant pentru a ridica și poate fi un motiv pentru care recursie este adesea înțeles greșit.

EDIT: Acest lucru nu a fost un dig la răspunsul lui Dav - nu am văzut că răspunsul atunci când am postat acest

Publicat 04/05/2010 la 12:29
sursa de către utilizator

voturi
3

Un exemplu: O definiție recursivă a unei scări este: O scara se compune din: - un singur pas și o scară (recursivitate) - sau doar o singură etapă (terminare)

Publicat 04/05/2010 la 14:34
sursa de către utilizator

voturi
3

Ei bine, asta este o definiție destul de decente ai. Și wikipedia are o definiție prea bun. Așa că voi adăuga o altă definiție (probabil, mai rău) pentru tine.

Când oamenii se referă la „recursivitate“, ei de obicei, vorbesc despre o funcție care le-am scris care se solicită în mod repetat, până când se face cu activitatea sa. Recursivitatea poate fi de ajutor atunci când traversează ierarhii în structuri de date.

Publicat 04/05/2010 la 12:27
sursa de către utilizator

voturi
3

Pentru a recurse o problemă rezolvată: nu face nimic, ai terminat.
Pentru a recursiv pe o problemă deschisă: face pasul următor, apoi recursiv pe restul.

Publicat 06/08/2008 la 04:32
sursa de către utilizator

voturi
2

Aceasta este o întrebare veche, dar vreau să adăugați un răspuns din punct de vedere logistic (de exemplu, nu din punctul de vedere al corectitudinii algoritm sau un punct de vedere al performanței).

Eu folosesc Java pentru muncă, și Java nu acceptă funcția imbricată. Ca atare, dacă vreau să fac recursivitate, s-ar putea să definească o funcție externă (care există numai pentru că codul meu lovește împotriva regimului birocratic Java), sau s-ar putea să refactor codul cu totul (care-mi place să fac).

Astfel, de multe ori am evita recurenței și funcționare utilizarea stivă în schimb, pentru că recursivitatea în sine este, în esență, o operațiune de stivă.

Publicat 30/08/2014 la 11:09
sursa de către utilizator

voturi
2

O funcție recursive este o funcție care conține un apel la sine. Un struct recursiv este un struct care conține o instanță de sine. Puteți combina cele două ca o clasă recursiv. Partea cheie a unui element recursiv este că acesta conține o instanță / apel de la sine.

Luați în considerare două oglinzi cu care se confruntă reciproc. Am văzut efectul infinit elegant care le fac. Fiecare reflecție este o instanță a unei oglinzi, care este conținută într-o altă instanță a unei oglinzi, etc. Oglinda care conține o reflectare în sine este recursivitate.

Un arbore binar de căutare este un exemplu bun de programare recursivitatii. Structura este recursivă cu fiecare nod care conține 2 instanțe ale unui nod. Funcții pentru a lucra pe un arbore binar de căutare sunt , de asemenea , recursiv.

Publicat 04/05/2010 la 17:46
sursa de către utilizator

voturi
2

În limba engleză: Să presupunem că puteți face 3 lucruri:

  1. Ia un măr
  2. Notați semne de concordanță
  3. Count mărci pontaj

Ai o mulțime de mere în fața ta pe o masă și vrei să știi cât mai multe mere sunt.

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

Procesul de a repeta același lucru până când terminat este numit recursivitate.

Sper că acest lucru este „englezesc simplu“ răspunsul pe care îl căutați!

Publicat 04/05/2010 la 14:09
sursa de către utilizator

voturi
1

Cea mai simplă definiție recursivitatii este „auto-referință“. O funcție care se referă la ea însăși, adică apelurile în sine este recursiv. Cel mai important lucru de a păstra în minte, este faptul că o funcție recursivă trebuie să aibă un „caz de bază“, adică o condiție că, dacă adevărat provoacă să nu se numească, și, astfel, să rezilieze recurență. În caz contrar, va avea recursivitate infinita:

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

Publicat 04/05/2010 la 17:10
sursa de către utilizator

voturi
1

Recursivitatea este atunci când aveți o operație care se utilizează. Este, probabil, va avea un punct de oprire, în caz contrar s-ar continua pentru totdeauna.

Să presupunem că doriți să căutați un cuvânt în dicționar. Ai o operație numită „look-up“, la dispoziția dumneavoastră.

Prietenul tău spune: „Aș putea lingură într-adevăr niște budincă chiar acum!“ Nu știi ce înseamnă, astfel încât să se uite în sus „lingură“ în dicționar, și se citește ceva de genul:

Spoon: substantiv - o ustensilă cu o lingura rundă la sfârșitul anului. Spoon: verb - de a folosi o lingură pe ceva Spoon: verb - să se ghemui îndeaproape din spate

Acum, fiind că într-adevăr nu ești bun cu limba engleză, acest lucru vă indică în direcția cea bună, dar ai nevoie de mai multe informații. Deci, selectați „ustensilă“ și „cuddle“ pentru a căuta pentru unele mai multe informații.

Flirtați: verb - la ghemui ustensilelor: substantiv - un instrument, de multe ori un tacam

Hei! Știi ce este tremurat, și nu are nimic de-a face cu budinca. De asemenea, știți că budinca este ceva ce mananci, asa ca are sens acum. Prietenul tău trebuie să vrea să mănânce budinca cu o lingura.

Bine, bine, acesta a fost un exemplu foarte șchiop, dar ilustrează (probabil prost) că cele două părți principale ale recursivitate. 1) Se folosește în sine. În acest exemplu, nu prea au privit un cuvânt semnificativ până când nu înțelege, și care ar putea însemna uita în sus mai multe cuvinte. Acest lucru ne aduce la punctul doi, 2) Se oprește undeva. Ea trebuie să aibă un fel de bază la caz. Altfel, ai ajunge doar să privească în fiecare cuvânt în dicționar, care, probabil, nu este prea util. Baza noastra de caz a fost că ai suficiente informații pentru a face o legătură între ceea ce anterior au și nu au înțeles.

Exemplul tradițional, care a dat este factorial, unde 5 este factorial * 2 * 1 3 * 4 * 5 (care este de 120). Cazul de bază ar fi 0 (sau 1, în funcție). Deci, pentru orice număr întreg n, faceți următoarele

este n egal cu 0? întoarce 1 în caz contrar, reveniți n * (factorial n-1)

să facem acest lucru cu exemplul 4 (pe care o cunoaștem înainte de timp este de 1 * 2 * 3 * 4 = 24).

factorial 4 ... este 0? nu, așa că trebuie să fie de 4 * factorial 3, dar ceea ce este factorial 3? este de 3 * factorial de 2 factorial de 2 este de 2 * factorial de 1 factorial 1 este 1 * factorial de la 0 și știm factorial de la 0! :-D e 1, e factorialului definiția 1 este 1 * factorial de 0, care a fost de 1 ... deci 1 * 1 = 1 factorial 2 este 2 * factorial 1, care a fost de 1 ... deci 2 * 1 = 2 factorial 3 este 3 * factorial 2, care a fost de 2 ... deci 3 * 2 = 6 factorial 4 (in sfarsit !!) este de 4 * factorial 3, care a fost de 6 ... 4 * 6 este 24

Factorial este un simplu caz de „caz de bază, și se folosește“.

Acum, am observat încă de lucru pe factorial 4 întregul drum în jos ... Dacă ne-am dorit factorial 100, ne-ar trebui să meargă tot drumul până la 0 ... care ar putea avea o mulțime de deasupra capului să-l. În același mod, dacă vom găsi un cuvânt obscur pentru a căuta în dicționar, ar putea dura căutând alte cuvinte și de scanare pentru indicii de context, până când vom găsi o legătură suntem familiarizați cu. Metodele recursive poate dura o lungă perioadă de timp pentru a lucra drumul lor prin intermediul. Cu toate acestea, atunci când acestea sunt utilizate în mod corect, și înțelese, ei pot face munca complicate surprinzător de simplu.

Publicat 04/05/2010 la 17:04
sursa de către utilizator

voturi
1

Un mare număr de probleme poate fi gândit în două tipuri de piese:

  1. cazuri de bază, care sunt lucruri elementare pe care le pot rezolva doar prin uita la ei, și
  2. cazuri recursive, care construiesc o problemă mai mare din bucăți mai mici (elementare sau de altă natură).

Deci, ce este o funcție recursivă? Ei bine, asta în cazul în care aveți o funcție care este definită în termeni de sine, în mod direct sau indirect. OK, că sună ridicol, până îți dai seama că este sensibil pentru problemele de tipul celor descrise mai sus: când a rezolva cazurile de bază în mod direct și să se ocupe de cazurile recursive prin utilizarea de apeluri recursive pentru a rezolva bucăți mai mici ale problemei încorporate în.

Exemplul cu adevărat clasic de cazul în care aveți nevoie de recurență (sau ceva care miroase foarte mult ca ea) este atunci când ai de a face cu un copac. Frunzele copacului sunt cauza de bază, iar ramurile sunt cazul recursiv. (În pseudo-C.)

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

Cel mai simplu mod de a imprima acest lucru în scopul este de a utiliza recursivitate:

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

Este mort ușor de văzut că va merge, deoarece este de cristal clar. (Echivalentul non-recursive este destul de mult mai complex, care necesită o structură de stivă pe plan intern pentru a gestiona lista de lucruri pentru a procesa.) Ei bine, presupunând că nimeni nu a făcut o conexiune circulară desigur.

Matematic, truc pentru a arata ca recursivitate este îmblânzită este să se concentreze pe găsirea unei valori pentru dimensiunea argumentelor. De exemplu nostru copac, cel mai simplu este metrica adâncimea maximă a arborelui sub nodul curent. La frunze, este zero. La o ramură cu frunze numai sub ea, este una, etc Apoi, puteți arăta pur și simplu că există strict ordonate secventa de dimensiunea argumentelor că funcția este invocat, pentru a fi procesa copac; argumentele pentru apelurile recursive sunt întotdeauna mai „mici“, în sensul metricii decât argumentul la apelul global. Cu o valoare cardinal strict descrescătoare, te sortate.

Este de asemenea posibil să aibă recursivitate infinit. Asta e murdar și în mai multe limbi nu va funcționa, deoarece stiva explodează. (În cazul în care nu funcționează, motorul de limbă trebuie să fie determinarea că funcția într-un fel nu se întoarce și este capabil, prin urmare, de a optimiza departe păstrarea stivei chestii Tricky în general;. Coada-recursivitate este doar modul cel mai banal de a face acest lucru .)

Publicat 04/05/2010 la 16:29
sursa de către utilizator

voturi
1

Recursivitatea este tehnica de a defini o funcție, un set sau un algoritm în termeni de sine.

De exemplu

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

Acum, ea poate fi definită ca recursiv: -

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

În termeni de programare, atunci când o funcție sau metodă se numește în mod repetat, până când unele condiții specifice devine îndeplinită, acest proces este numit Recursivitate. Dar trebuie să existe o condiție de terminare și funcția sau metoda nu trebuie să intre într-o buclă infinită.

Publicat 04/05/2010 la 16:22
sursa de către utilizator

voturi
1

Recursivitate în calcul este o tehnică utilizată pentru a calcula un efect rezultat sau secundar ca urmare randamentul normal dintr-o singură funcție (metodă, procedeu sau bloc) invocare.

Funcția recursivă, prin definiție, trebuie să aibă capacitatea de a se invoca în mod direct sau indirect (prin alte funcții), în funcție de o condiție de ieșire sau condiții nu sunt îndeplinite. În cazul în care o condiție de ieșire este îndeplinită în special de invocare revine la apelant-l lui. Aceasta se continuă până invocarea inițială este returnată din, moment în care rezultatul sau lateral efectul dorit va fi disponibil.

Ca un exemplu, aici este o funcție pentru a efectua algoritmul Quicksort în Scala ( copiat de la intrarea pe Wikipedia pentru Scala )

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

În acest caz, starea de ieșire este o listă goală.

Publicat 04/05/2010 la 15:14
sursa de către utilizator

voturi
1

Funcția se numesc sau de a folosi propria definiție.

Publicat 04/05/2010 la 14:59
sursa de către utilizator

voturi
1

Orice algoritm prezintă structural de recurență pe un tip de date în cazul în care, practic , constă dintr - un comutator de declarație cu un caz pentru fiecare caz de tipul de date.

de exemplu, atunci când lucrați pe un tip

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

un algoritm recursiv structural ar avea 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

acest lucru este într-adevăr cel mai evident mod de a scrie orice algorith, care funcționează pe o structură de date.

acum, când te uiți la numerele întregi (bine, numerele naturale), astfel cum sunt definite folosind axiomele Peano

 integer = 0 | succ(integer)

vedeți că un algoritm recursiv structurale pe numere întregi arata ca acest lucru

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

prea-bine-cunoscute funcția factorial este despre exemplul cel mai banal al acestei forme.

Publicat 04/05/2010 la 14:53
sursa de către utilizator

voturi
1

hei, îmi pare rău dacă părerea mea este de acord cu cineva, eu doar încerc să explic recursivitate în limba engleză simplu.

să presupunem că aveți trei manageri - Jack, John și Morgan. Jack administrează 2 programatori, John - 3, și Morgan - 5. aveți de gând să dea fiecare manager de 300 $ și vreau să știu ce-ar costa. Răspunsul este evident - dar ce se întâmplă dacă 2 dintre angajații Morgan-uri sunt, de asemenea, manageri?

Aici vine recursivitatea. începe din partea de sus a ierarhiei. costul estivală este de 0 $. începe cu Jack, Apoi, verificați dacă are ca manageri de angajați. dacă găsiți oricare dintre ele sunt, verificați dacă au ca manageri angajați și așa mai departe. Adăugați 300 $ la costul estivala de fiecare dată când găsiți un manager. când ați terminat cu Jack, du-te la John, angajații săi și apoi la Morgan.

Nu vei ști niciodată, cât de mult cicluri va merge inainte de a primi un răspuns, deși știi cât de mulți manageri ai și cât de multe buget poate cheltui.

Recursivitatea este un copac, cu ramuri și frunze, numite părinți și copii, respectiv. Când utilizați un algoritm recurenței tine mai mult sau mai puțin conștient sunt construirea unui copac din datele.

Publicat 04/05/2010 la 13:50
sursa de către utilizator

voturi
1

În limba engleză, recurențe înseamnă să se repete din nou și din nou someting.

Într-un exemplu de programare este apelarea funcției în sine.

Uita-te la următorul exemplu de calcul factorial al unui număr:

public int fact(int n)
{
    if (n==0) return 1;
    else return n*fact(n-1)
}
Publicat 04/05/2010 la 13:48
sursa de către utilizator

voturi
1

Recursivitatea este procesul în cazul în care un apel de metodă pentru a putea atare, ci constituie de a efectua o anumită sarcină. Reduce redundency de cod. Cele mai multe funcții recurssive sau metode trebuie să aibă o condifiton de a sparge recussive suna adică opri din autointitulat dacă este îndeplinită o condiție - aceasta împiedică crearea unei bucle infinite. Nu toate funcțiile sunt potrivite pentru a fi utilizate recursiv.

Publicat 04/05/2010 la 13:42
sursa de către utilizator

voturi
1

ei un mod de a face lucruri peste si peste la infinit, astfel încât fiecare opțiune este utilizată.

de exemplu, dacă doriți să obțineți toate link-urile de pe o pagină HTML pe care va dori să aibă recurența pentru că atunci când veți obține toate link-urile de la pagina 1 veți dori să obțineți toate link-urile de pe fiecare din link-urile gasite pe prima pagină. apoi pentru fiecare link către un newpage veți dori aceste link-uri și așa mai departe ... cu alte cuvinte, este o funcție care se solicită din interior în sine.

atunci când faci acest lucru ai nevoie de o modalitate de a ști când să se oprească sau altceva ce va fi într-o buclă fără sfârșit, astfel încât să adăugați un număr întreg param la funcția pentru a urmări numărul de cicluri.

în c # vei avea ceva de genul:

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;
}
Publicat 04/05/2010 la 13:02
sursa de către utilizator

voturi
1

„Dacă am un ciocan, face ca totul să pară un cui.“

Recursivitatea este o strategie de rezolvare a problemelor pentru mari probleme, în cazul în care la fiecare pas doar „rândul său , 2 lucruri mici , într - un singur lucru mai mare,“ de fiecare dată cu același ciocan.

Exemplu

Să presupunem că biroul este acoperit cu o mizerie dezorganizat de 1024 lucrări. Cum faci un teanc ordonat, curat de hârtii de mizerie, folosind recursivitatea?

  1. Divide: Imprastiati toate foile afară, astfel încât să aveți doar o singură foaie , în fiecare „stivă“.
  2. A cuceri:
    1. Du-te în jurul valorii, punând fiecare foaie pe partea de sus a unui alt tip de foaie. Acum aveți stive de 2.
    2. Du-te în jurul valorii, punând fiecare 2-stack pe partea de sus a unui alt 2-stivă. Acum aveți stive de 4.
    3. Du-te în jurul valorii, punând fiecare 4-stivuiesc un alt 4-stivă. Acum aveți stive de 8.
    4. ... iar si iar ...
    5. Ai acum un teanc mare de 1024 de coli!

Observați că acest lucru este destul de intuitiv, în afară de numărare tot (ceea ce nu este strict necesar). Este posibil să nu merge tot drumul până la 1 stive de coli, în realitate, dar ai putea și ar funcționa în continuare. Partea importantă este ciocanul: Cu bratele, puteți pune întotdeauna un teanc de pe partea de sus a celeilalte pentru a face un stack mai mare, și nu contează (în termen de motiv), fie cât de mare este stiva.

Publicat 04/05/2010 la 12:54
sursa de către utilizator

voturi
1

Recursivitatea așa cum se aplică de programare este de asteptare, practic, o funcție din interiorul propriei definiții (în interiorul său), cu diferiți parametri, astfel încât să se realizeze o sarcină.

Publicat 04/05/2010 la 12:25
sursa de către utilizator

voturi
1

Vrei să-l utilizați oricând aveți o structură arborescentă. Este foarte util în lectură XML.

Publicat 21/08/2008 la 14:18
sursa de către utilizator

voturi
1

Mario, eu nu înțeleg de ce ai folosit recursivitate pentru acest exemplu .. De ce nu pur și simplu în buclă prin fiecare intrare? Ceva de genul:

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;
}

Metoda de mai sus ar fi mai rapid, și este mai simplu. Nu este nevoie să utilizați recursivitate în loc de o buclă simplă. Cred că aceste tipuri de exemple este motivul pentru care recursivitatea devine un rap rău. Chiar și exemplul funcția factorial canonic este mai bine pusă în aplicare cu o buclă.

Publicat 06/08/2008 la 04:19
sursa de către utilizator

voturi
0

De fapt, o soluție mai bună pentru recursiv factorial ar trebui să fie:

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);
}

Deoarece această versiune este Coada recursive

Publicat 21/08/2008 la 14:39
sursa de către utilizator

voturi
0

Eu folosesc recursivitate. Ce are de a face cu având un grad CS ... (care nu fac, de altfel)

Utilizări comune am găsit:

  1. sitemaps - recurse prin sistemul de fișiere începând de la rădăcină documentul
  2. păianjeni - crawling printr - un site pentru a găsi adresa de e - mail, link - uri, etc.
  3. ?
Publicat 06/08/2008 la 04:13
sursa de către utilizator

voturi
0

Am creat o funcție recursivă pentru a înlănțui o listă de șiruri cu un separator între ele. Eu îl folosesc în special pentru a crea expresii SQL, prin trecerea unei liste de domenii ca „ elemente “ și o „ virgulă + spațiu “ ca separator. Iată funcția (Se utilizează anumite tipuri de date native Borland Builder, dar poate fi adaptat pentru a se potrivi cu orice alt mediu):

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;
}

Eu numesc acest fel:

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

Imaginați - vă că aveți o matrice „numit câmpuri “ cu aceste date în interiorul acestuia: „ ALBUMNAME “, „ releasedate “, „ labelId “. Apoi , va apela funcția:

ArrangeString(fields, 0, ", ");

Deoarece funcția începe să funcționeze, „variabila rezultat “ primește valoarea poziției 0 din matrice, care este „ ALBUMNAME “.

Apoi verifică dacă poziția pe care se ocupă de este ultima. După cum nu este, atunci concateneaza rezultatul cu separatorul și rezultatul unei funcții, care, oh Dumnezeu, este aceeași funcție. Dar, de data aceasta, a verifica it afară, acesta se numesc adăugând 1 la poziția.

ArrangeString(fields, 1, ", ");

Se tot repetă, creând un teanc LIFO, până când ajunge la un punct în care poziția de a fi tratată este ultima, astfel încât funcția returnează numai elementul pe această poziție pe listă, nu mai concatenand. Apoi gramada este concatenate înapoi.

Am înţeles? Dacă nu, am un alt mod de a explica. : O)

Publicat 06/08/2008 la 04:00
sursa de către utilizator

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