![]() |
Índex |
void Castig(void)
{ printf("Aixó és un càstig infinit"); Castig();
|
Aquesta funció es crida a si mateixa. Cada vegada que es crida crea una instància nova en memòria de la funció.
Com que no té cap instrucció de sortida no para de cridar-se a si mateixa i de crear instàncies de si mateixa. El programa peta quan s'omple la memòria de funcions en execució (pila del programa). És un exemple d'allò que no hem de fer mai amb funcions recursives.
Les funcions recursives poden rebre i tornar paràmetres com qualsevol altra funció.
Normalment es fa ús d'un paràmetre per controlar, entre d'altres coses, quan a de aturar-se de cridar-se a sí mateixa
La funció següent escriu en pantalla números naturals
de manera decreixent. La funció para de cridar-se a si mateixa quan
el valor de Numero es igual a zero:
#include <stdio.h>
void CuentaAlReves(int Numero); main()
void CuentaAlReves (int Numero)
if (Numero >0) CuentaAlReves(Numero-1);
|
Observem en la figura quin és el flux d'execució i creació simultània de funcions CuentaAlReves.
És fa notar que cada crida recursiva fa que s'executi una nova instància de la funció diferent de les altres, de manera que cadascuna d'elles manté actives les seves pròpies variables.
També és curiós observar que les instruccions de FIN de funció s'executen al contrari de com tendeix un a pensar que haurien d'executar-se.
La funció EscribeNumeros es quasi igual que la funció
anterior, però els números s'escriuen en ordre creixent:
#include <stdio.h>
void EscribeNumeros(int Num);
void EscribeNumeros(int Num)
|
Si observem que la crida recursiva es fa abans que s'executi la instrucció d'escriure entendrem perquè ara els números s'escriuen en ordre creixent.
Allò que no s'ha de fer: si una situació no és recursiva no s'ha d'utilitzar la recursivitat per a programar-la. Cap dels programes anteriors correspon a situacions realment recursives, si no a situacions merament repetitives. No s'haurien d'haver escrit així.
Allò que si s'ha de fer: El factorial
d'un número n es n! = n(n-1)(n-2)x....x3x2x1. Aquesta forma de definir-lo
no és recursiva. Però també pot definir-se en la manera
n! = n(n-1)! i aquesta forma si que és recursiva. Es pot escriure
la funció recursiva següent.
#include <stdio.h>
long double Factorial(int Num); main()
printf("Escribe un número entero positivo
");
long double Factorial(int Num)
if (Num>1) Fact=Num*Factorial(Num-1); return Fact;
|
És convenient que com exercici facis el diagrama de flux d'execució
d'aquesta funció amb una crida Factorial(5).
Es disposa d'una torre com la de la figura esquerra, de qualsevol número de peces, i de tres posicions, esquerra, mig i dreta. El joc consisteix en pasar la torre d'una posició a una altra movent una peça cada vegada, però amb la condició de que no es poden col·locar peces més petites a sota de peces més grans.
Farem ara l'explicació del l'algoritme recursiu que realitza
el joc de les torres:
Torre d'una peça | Torre a la dreta (d'una peça). |
Torre de dues peces | Torre a la dreta (d'una peça).
Peça de l'esquerra al mig. Torre a l'esquerra (d'una peça). Peça del mig a la dreta. Torre a la dreta (d'una peça). |
Torre de tres peces | Torre a la dreta (de dues peces).
Peça de l'esquerra al mig. Torre a l'esquerra (de dues peces). Peça del mig a la dreta. Torre a la dreta (de dues peces). |
Torre de quatres peces | Torre a la dreta (de tres peces).
Peça de l'esquerra al mig. Torre a l'esquerra (de tres peces). Peça del mig a la dreta. Torre a la dreta (de tres peces). |
Aquests passos es poden seguir indefinidament i passar qualsevol número
de peces d'un costat a un altre. En el joc, i per a una torre de n peces
es realitzen cinc tipus de moviment. Tres moviments corresponen a una crida
al joc d'una torre de n-1 peces, un moviment a la esquerra i dos moviments
a la dreta. Així el programa que realitza aquest joc és el
següent:
#include <STDIO.H>
void HanoiEsquerra(int peces);
main()
void HanoiDreta(int peces)
void HanoiEsquerra(int peces)
|
Suposem que tenim un vector que volem ordenar i que la primera posició és Esquerra i la última és Dreta.
Es tracta de dividir els valors que tenim entre Esquerra i Dreta en dos blocs de manera que tots els nombres del primer bloc siguin més petits que els del segon bloc.
S'agafa el valor que es troba al mig, entre Esquerra i Dreta i cerquem:
|
![]() |
Bé, els valors finals per a els índex són i = 5 y j =3. Així ja tenim el vector dividit en dues parts. Tots els elements de la primera part són més petits que els de la segona. Podem seguir dividint la primera part en dues i la segona també i així succesivament fins que el vector estigui ordenat.
D'aquesta manera podem plantejar la funció recursiva següent
que fa diverses crides recursives segons el número d'elements del
vector.
#include <stdio.h>
void OrdenaRapido(int Esquerra,int Dreta, int vecrtor[]); main()
int vectorAOrdenar[10]= {0,1,3,9,4,2,-2,-3,-4,10};
return 0;
void OrdenaRapido(int Esquerra,int Dreta, int vector[])
i=Esquerra; j=Dreta;
|
#include <conio.h> #include <subru.h> struct Cicle { char Nom[46]; char Assigs[45]; char Adreca[35]; char Telefon[10]; char NotaMetodologia[4]; char NotadBase[4]; } M3PI[16]={ {"GONZALEZ,GOMEZ;ELVIRA","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","SABADELL s/n"}, {"CUEVAS,SORIANO;JESUS","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","BARCELONA 127"}, {"ALVAREZ,CANTERO;FCO. JOSE","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","TERRASSA 34"}, {"TORRES,ANTON;GINES","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","TAMARIT 23"}, {"COBO,SANCHEZ;ANTONIO","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","SOCIAS 56"}, {"SANCHEZ,EMBID;JUAN ANTONIO","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","BARBERA 3"}, {"CABALLE,COSTA;ESTEL","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","STA TERPETUA 12"}, {"ADRIAN,ESCUDERO;SARA","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","LUMBRERES 35"}, {"ALEPUZ,MASO;ALEJANDRO","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","CANDELES 15"}, {"ALEPUZ,MASO;JAVIER","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","CARBONERS 17"}, {"UCEDA,MADIALDEA;M¦ ANTONIA","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","PANADERS 19"}, {"GONZALEZ,CASTILLO;RAMONF.","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","TALABARDERS 123"}, {"BONILLA,BERNAL;SILVIA","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","QUINCALLERS 12"}, {"ANGELET,GIMENO;JAVIER","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","PEIXETERS 11"}, {"POLO,VILLATORO;MANUEL","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","PELOTARIS 34"}, {"LUNA,PEREZ,JUAN DIEGO","M3METM3PRCM3ANGM3INBM3DBSM3TEXM3LAB","TAMVIAIRES 128"} };En la capçalera tenim declarades tres funcions:
void Imprimeix(void); int OrdreNoms(const void *a, const void *b); int OrdreAdreca(const void *a,const void *b); La funció principal s'encarrega de imprimir i ordenar els registres int main(void) { Imprimeix(); qsort( (void *)M3PI, 16, sizeof(M3PI[0]),OrdreNoms); Imprimeix(); qsort( (void *)M3PI, 16, sizeof(M3PI[0]),OrdreAdreca); Imprimeix(); return 0; } Per últim tenim les tres funcions: void Imprimeix(void) { int i; char Eti1[45]="Nom"; char Eti2[45]="Adreça"; clrscr(); cprintf("LLISTA DE DADES\r\n\n"); cprintf("Re %-40s %-35s\r\n\n",Eti1,Eti2); for(i=0;i<16;i++) cprintf("%2d %-40s %-35s\r\n",i,M3PI[i].Nom,M3PI[i].Adreca); EsperaTecla(0); } int OrdreNoms(const void *a, const void *b){ return(strcmp((char *)a, (char *)b)); } int OrdreAdreca(const void *a, const void *b){ return(strcmp((char *)a+91, (char *)b+91)); }La funció qsort està definida en la forma següent:
void qsort(void *base, size_t nelem, size_t amplada, int (*fcmp)(const void *, const void *);Aquesta funció inclou 4 paràmetres:
return(strcmp((char *)a, (char *)b));Si volem ordenar per un altre camp hem de desplaçar el punter tants llocs com sigui necessari, així si volen ordenar per adreces hem de fer el següent retorn:
return(strcmp((char *)a+91, (char *)b+91));Si comptes veuràs que totes les adreces comencen al lloc 91.
Per a dibuixar les dues primeres branques amb un llapis que no es lleva
del paper haurem de realitzar els següents moviments:
Gira a la Esquerra 45 Avança longitud de rama Recula longitud de rama Gira a la Dreta 90 Avança longitud de rama Recula longitud de rama Gira a la Esquerra 45Però després d'haver dibuixat la primera branca haurem de dibuixar un arbre en el que les primeres branques tenen la meitat de longitud. Es a dir que hem de fer unes crides de tipus recursius per a dibuixar totes les branques i en tota la seva profunditat.
Un algoritme que dibuixa aquest arbre és el següent:
Procediment Arbre(Longitud de rama)
Si rama > 2 Gira a la Esquerra 45 Avanza longitud de rama Arbol(Longitud de rama dividida por 2) Retrocede longitud de rama Gira a la Dreta 90 Avanza longitud de rama Arbol(Longitud de rama dividida por 2) Retrocede longitud de rama Gira a la Esquerra 45 Fi del si Fi del procediment |
Una solució en C es la següent:
#include <graphics.h> #include <stdio.h> #include <math.h> #include <conio.h> #include <graphics.h> void PantallaGrafica(void); void PantallaTexto(void); void Arbol(float Rama); double Xinicio=0,Yinicio=0; double Xfin,Yfin; double Direccion=0; double pi; main(){ pi=atan(1)*4; PantallaGrafica(); cleardevice(); Xinicio=(int)(getmaxx()/2); Yinicio=(int)(getmaxy()/2) + 20; Direccion=-90; Arbol(100); getchar(); PantallaTexto(); } void Arbol(float Rama){ if (Rama >= 2) { Direccion=Direccion+45; //Avanza rama Xfin=Xinicio+Rama*cos(pi*Direccion/180); Yfin=Yinicio+Rama*sin(pi*Direccion/180); line((int)Xinicio,(int)Yinicio,(int)Xfin,(int)Yfin); Xinicio=Xfin;Yinicio=Yfin; Arbol(Rama/2); //Retrocede rama Xfin=Xinicio+Rama*cos(pi*Direccion/180 + pi); Yfin=Yinicio+Rama*sin(pi*Direccion/180 + pi); line((int)Xinicio,(int)Yinicio,(int)Xfin,(int)Yfin); Xinicio=Xfin;Yinicio=Yfin; Direccion=Direccion-90; //Avanza rama Xfin=Xinicio+Rama*cos(pi*Direccion/180); Yfin=Yinicio+Rama*sin(pi*Direccion/180); line((int)Xinicio,(int)Yinicio,(int)Xfin,(int)Yfin); Xinicio=Xfin;Yinicio=Yfin; Arbol(Rama/2); //Retrocede longitud Xfin=Xinicio+Rama*cos(pi*Direccion/180 + pi); Yfin=Yinicio+Rama*sin(pi*Direccion/180 +pi); line((int)Xinicio,(int)Yinicio,(int)Xfin,(int)Yfin); Xinicio=Xfin;Yinicio=Yfin; Direccion=Direccion+45; } } void PantallaGrafica(void) { int gdriver = DETECT, gmode, errorcode; initgraph(&gdriver, &gmode, ""); errorcode = graphresult(); if (errorcode != grOk) { cprintf("Graphics error: %s\n", grapherrormsg(errorcode)); cprintf("Press any key to halt:"); getch(); exit(1); } } void PantallaTexto(void) { closegraph(); }
a)
|
b)
|
c)
|
Tramvia
Tramvi
Tramv
Tram
Tra Tr T |
Tramvia
ramvia
amvia
mvia
via ia a |
Tramvia
Travia
Tria
Ta
|
2.- Fes una funció recursiva que ens digui si un string és un palíndrom o no. Recorda que un palíndrom és una frase que es llegeix igual en un sentit (Esquerra-Dreta) que en l'altre sentit (Dreta-Esquerra).
dabalearrozalazorraelabad
3.- Fes una funció recursiva que ens digui si dos strings són iguals però escrits al contrari l'un de l'altre.
4.- Fibonacci: Les liebres es reprodueixen de la següent manera:
·Des de que neixen fins que tenen fills passen dos mesos.
·Després
tenen una parella cada més.
Així si partim d'una primera parella podem veure que es reprodueixen de la següent manera:
1r
mes
|
2n
mes
|
3r
mes
|
4t
mes
|
5é
mes
|
2
llebres
|
4
llebres
|
6
llebres
|
10
llebres
|
16
llebres
|
Així tenim que cada podem calcular el número de llebres que tenim un cert mes si prenem les que teníem els dos mesos anteriors i les sumem.
Tenim una successió en la que an+2=an+an+1.
Fes una funció recursiva que rebi quants mesos han transcorregut i que ens escrigui la taula anterior fins a aquest més.
A(0,n) = n +1, A(m,0) = A(m-1,1), A(m,n) = A(m-1,A(m,n-1))
Fes la funció recursiva que rebi m i n i ens calculi A(m,n).