






Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Prepara tus exámenes
Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Prepara tus exámenes con los documentos que comparten otros estudiantes como tú en Docsity
Encuentra los documentos específicos para los exámenes de tu universidad
Estudia con lecciones y exámenes resueltos basados en los programas académicos de las mejores universidades
Responde a preguntas de exámenes reales y pon a prueba tu preparación
Consigue puntos base para descargar
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Comunidad
Pide ayuda a la comunidad y resuelve tus dudas de estudio
Ebooks gratuitos
Descarga nuestras guías gratuitas sobre técnicas de estudio, métodos para controlar la ansiedad y consejos para la tesis preparadas por los tutores de Docsity
El concepto de recursividad en programación y cómo se aplica a algoritmos de búsqueda, como la búsqueda binaria. Se incluyen ejemplos en c y se comparan con algoritmos de repetición. El documento también incluye un ejemplo de la función recursiva factorial.
Tipo: Apuntes
1 / 10
Esta página no es visible en la vista previa
¡No te pierdas las partes importantes!







Activitat 5: Disseny modular. Recursivitat. .a
**1. Disseny de funcions recursives.
A l’hora de crear programes complexos, un dels aspectes que diferencia el bon programador de l’aficionat és la seva capacitat de fer algorismes eficients. O sigui, que siguin capaços de resoldre el problema plantejat en el mínim de passes.
En el cas d’un programa, això significa la necessitat d’executar el mínim nombre d’instruccions possible. Certament, si el resultat ha de ser exactament el mateix, sempre serà millor fer una tasca en 10 passes que no pas en 20, intentant evitar passes que en realitat són innecessàries. Per tant, l’etapa de disseny d’un algorisme és força important i cal pensar bé una estratègia eficient. Ara bé, normalment, els algorismes més eficients també són més difícils de pensar i codificar, ja que no sempre són evidents.
Un exemple molt senzill d’això és la resolució del problema següent:
Suposeu que una amiga apunta un número entre el 0 i el 99 en un full de paper i vosaltres l’heu d’endevinar. Cada cop que contesteu, us dirà si el valor que heu dit és més gran o més petit que el que heu d’endevinar.
Quina estratègia seguiríeu per assolir-ho?
Cal pensar un algorisme a seguir per resoldre aquest problema. Una aproximació molt ingènua podria ser anar dient tots els valors un per un, començant pel 0. està clar que quan arribeu al 99 l’haureu endevinat. En el millor cas, si havia escrit el 0, encertareu a la primera, mentre que en el pitjor cas, si havia escrit el 99, necessitareu 100 intents. Si estava pel mig, potser amb 40-70 n’hi ha prou. Aquest seria un algorisme que fa el fet i és molt senzill, però no gaire eficient. Anar provant valors a l’atzar en lloc de fer això tampoc millora gran cosa el procés, i ve a ser el mateix. De ben segur, si mai heu jugat a aquest joc, el que heu fet és ser una mica més astuts i començar per algun valor del mig. En aquest cas, per exemple, podria ser el 50. Llavors, en cas de fallar, un cop sabeu si el valor secret és més gran o més petit que la vostra resposta, en l’intent següent provar un valor més alt o més baix, i anar fent això repetides vegades.
Generalment, la millor estratègia per endevinar un número secret entre 0 i N seria:
Si us hi fixeu, l’exemple que tot just s’acaba d’explicar, en realitat, no és més que un esquema de cerca dins una seqüència de valors, com pot ser dins d’un array, partint de la condició que tots els elements estiguin ordenats de més petit a més gran. Aquest algorisme ja el coneixem com cerca binària, també es pot anomenar cerca dicotòmica.
1.1 Aplicació de la recursivitat
Malauradament, sovint us trobareu que explicar de paraula la idea general d’una estratègia pot ser senzill, però traduir-la a instruccions de C ja no ho és tant.
Atès que cal anar repetint unes passes en successives iteracions, està més o menys clar que el problema plantejat per fer cerques eficients es basa en una estructura de repetició. Però no es recorren tots els elements i l’índex no s’incrementa un a un, sinó que es va canviant a valors molt diferents per cada iteració. No és un cas evident. Precisament, aquest exemple no s’ha triat a l’atzar, ja que és un cas en què us pot anar bé aplicar un nou concepte que permet facilitar la definició d’algorismes complexos on hi ha repeticions.
La recursivitat és una forma de descriure un procés per resoldre un problema de manera que, al llarg d’aquesta descripció, s’usa el procés mateix que s’està descrivint, però aplicat a un cas més simple.
La millor estratègia per endevinar un número secret entre 0 i N seria primer provar N/2.
O sigui, el procés d’ endevinar un número es basa en el procés d’intentar endevinar un número!
Això sembla fer trampes, ja és com usar la mateixa paraula que es vol definir a la seva pròpia definició. Però fixeu-vos en un detall molt important. Els nous usos del procés d’ “endevinar” són casos més simples , ja que primer s’endevina entre N valors possibles, després entre N/2 valors, després entre N/4, etc. Aquest fet no és casual i d’ell depèn poder definir un procés recursiu de manera correcta.
Si ens fixem el factorial de un numero n mes gran que 2 es pot expressar com ell mateix per el factorial de n-1:
2! = 2 * 1! 3! = 3 * 2! 4! = 4 * 3! 5! = 5 * 4! 6! = 6 * 5! 7! = 7 * 6! .......
Normalment, la definició matemàtica d’aquesta operació es fa de manera recursiva:
Així, doncs, fixeu-vos que el cas recursiu realitza un càlcul que depèn d’usar la pròpia definició de l’operació, però quan ho fa és amb un nou valor inferior a l’original, de manera que es garanteix que, en algun moment, es farà una crida recursiva que desembocarà en el cas base. Quan això passi, la cadena de crides recursives acaba. Una manera de veure això és crear el arbre de crides a la funció:
La seva implementació en C seria la següent. Ara bé, en aquest codi s’han afegit algunes sentències per escriure informació per pantalla, de manera que es vegi amb més detall com funciona un mètode recursiu. Veureu que, inicialment, es porten a terme un seguit d’invocacions del cas recursiu, un rere l’altre, fins que s’arriba a una crida que executa el cas base. És a partir de llavors quan, a mesura que es van executant les sentències return del cas recursiu, realment es va acumulant el càlcul. Una altra manera de veure-ho és depurant el programa.
int llegirNumeroPositiu ( char* , char*); long int factorial (int);
int llegirNumeroPositiu (char* missatge, char* missatgeError){ int n;
do{ puts(missatge); scanf(“%i”,&n); if (n<0) puts(missatgeError); }while (n<0);
return n; }
long int factorial (int n){ long int res;
if (n == 1) { //Cas base: Se sap el resultat directament
printf("Cas base: S’avalua a 1"); return 1;
}else { // Cas recursiu: invoca a la pròpia funció // El valor del nou paràmetre d’entrada ha de variar de // manera que es vagi aproximant al cas base
printf("Cas recursiu %i S’invoca el factorial”, n - 1); return = n * factorial(n 1);
} }
void main (void){ int n; long int fact;
n = llegirNumeroPositiu(“Factorial de: ”, “El numero ha de ser positiu.”); fact = factorial(n); printf(“El factorial de %li es: “,fact);
}
entre instruccions força més complexes que l’opció recursiva (un cop s’entén aquest concepte, és clar).
1.3 Recursivitat al joc de les torres de Hanoi:
Aquest exemple es una solució recursiva a l’antic joc de les torres de Hanoi, un dels exemples clàssics d’algorisme recursiu.
El joc parteix de tres bases A, B i C. En aquestes bases es poden posar discos, tots de mida diferent, de forma que mai pot estar un disc a sobre d'un altre disc més petit. Si tots els discos estan inicialment a la base A, el joc consisteix en traslladar-los d'un en un fins posar-los a la base C, mantenint sempre la regla que un disc mai estigui a sobre d'un altre més petit.
El següent programa implementa aquest procés amb la funció recursiva h anoi() :
#include <stdio.h> #include <stdlib.h>
void hanoi(int,char,char,char);
int main(){ int n;
system("clear"); printf("introduïu el nombre n de discos :"); scanf("%d",&n); fflush(stdin); hanoi(n,'A','C','B'); return 0; }
void hanoi(int n, char a, char c, char b){
if (n==1){ printf("%c->%c\t",a,c); return; } hanoi(n-1,A,B,C); hanoi(1,A,C,B); hanoi(n-1,B,C,A); }
Captura de l'execució del programa.
Cas base :
Si hi ha només un disc , el problema és trivial: és suficient moure l'únic disc d'A fins a C. Anomenarem a aquest algorisme hanoi(1,A,C,B), B no s’utilitza. Moviment: A -> C
Cas recursiu:
En el cas que hi hagi dos discos , es pot fer amb tres moviments simples (cas base):
A->B, A-> C, B-> C, és a dir, tenim un procediment per passar dos discos d'A a C utilitzant B com ajuda. Anomenarem a aquest algoritme hanoi(2,A,C,B ) (passar dos discos d'A a C fent servir B).
hanoi(2,A,B,C) hanoi(1,A,B,C)
hanoi(1,A,C,B) hanoi(1,B,C,A)
En general, si hi ha n, es pot fer:
hanoi(n–1,A,B,C); hanoi(1,A,C,B); hanoi(n–1,B,C,A);
Això és justament el que fa la funció recursiva hanoi():
void hanoi ( int n, char a, char c, char b){
if (n==1){ printf("%c->%c\t",A,C); return; } hanoi(n-1,A,B,C); hanoi(1,A,C,B); hanoi(n-1,B,C,A); }
En aquesta funció, el cas base o d’escapament és el cas trivial n = 1 en el qual s'imprimeix el moviment.
L’arbre de crides a la funció hanoi:
hanoi(4,A,C,B)
hanoi(3,A,B,C,) hanoi(1,A,C,B) hanoi(3,B,C,A)
h(2,A,C,B) h(1,A,B,C) h(2,C,B,A) h(2,A,C,B) h(1,B,C,A) h(2,C,B,A)
h(1,A,B,C) h(1,C,A,B) h(1,B,C,A) h(1,A,B,C) h(1,A,C,B) h(1,C,B,A) h(1,B,A,C) h(1,A,C,B) h(1,B,C,A) h(1,A,B,C) h(1,C,A,B) h(1,B,C,A)
Si executem el programa amb el valor 4 ens mostra 15 moviments:
A->B A->C B->C A->B C->A C->B A->B A->C B->C B->A
C->A B->C A->B A->C B->C
Que es corresponent a les següents crides de la funció:
A->B A->C B->C A->B C->A C->B A->B A->C B->C B->A C->A B->C A->B A->C B->C