El algoritmo iterativo es el método básico para resolver problemas con ordenadores. Aprovecha la rápida velocidad de computación de la computadora y es adecuado para operaciones repetidas, de modo que la computadora ejecuta repetidamente un conjunto de instrucciones (o ciertos pasos). Cada vez que se ejecuta este conjunto de instrucciones (o estos pasos), el valor de una variable. se deduce de su nuevo valor original.
Para utilizar algoritmos iterativos para resolver problemas, debemos realizar los siguientes tres aspectos:
Primero, determinar las variables de iteración. En los problemas que pueden resolverse mediante algoritmos iterativos, hay al menos una variable que directa o indirectamente deriva un nuevo valor a partir de un valor anterior. Esta variable es la variable de iteración.
En segundo lugar, establezca una relación iterativa. La llamada relación iterativa se refiere a la fórmula (o relación) de cómo derivar el siguiente valor a partir del valor anterior de una variable. El establecimiento de relaciones iterativas es la clave para resolver problemas iterativos y normalmente se puede lograr mediante recursividad o derivación inversa.
En tercer lugar, controlar el proceso iterativo. ¿Cuándo termina el proceso iterativo? Esta es una cuestión que debe tenerse en cuenta al escribir programas iterativos. El proceso iterativo no se puede repetir infinitamente. El control del proceso iterativo generalmente se puede dividir en dos situaciones: una es que el número requerido de iteraciones es un valor determinado y se puede calcular; la otra es que el número requerido de iteraciones no se puede determinar; En el primer caso, se puede construir un número fijo de bucles para controlar el proceso iterativo; en el segundo caso, es necesario analizar más a fondo las condiciones para finalizar el proceso iterativo.
Una nueva raza de conejos llega a una granja. Este tipo de conejo dará a luz un nuevo conejo cada mes a partir del segundo mes de vida, y el nuevo conejo se reproducirá de la misma forma. Si no murieron todos los conejos, ¿cuántos conejos había en la granja en diciembre?
Análisis: Este es un problema típico de recursividad. También podríamos suponer que el número de conejos en el primer mes es u 1, el número de conejos en el segundo mes es u 2 y el número de conejos en el tercer mes es u 3... Según el significado de la pregunta: "Este tipo de conejo es desde el nacimiento hasta A partir del próximo mes, nacerá un nuevo conejo cada mes".
u 1 = 1, u 2 = u 1 u 1 × 1 = 2, u 3 = u 2 u 2 × 1 = 4,…
Según esta regla, can Se resume la siguiente fórmula de recursión:
u n = u n - 1 × 2 (n ≥ 2)
Correspondiente a u n y U n-1, dos variables de iteración Y y X, la fórmula recursiva anterior se puede transformar en la siguiente relación iterativa:
y=x*2
x=y
Deje que la computadora repita esta relación iterativa 11 veces, puedes calcular el número de conejos en diciembre. El procedimiento de referencia es el siguiente:
cls
x=1
Para i=2 a 12
y=x*2
x=y
Siguiente I
Imprimir y
Fin
Ejemplo 2: Ameba mediante división simple Para reproducir cada división se necesitan 3 minutos. Coloque varias amebas en un recipiente que contenga ginseng líquido nutritivo. Después de 45 minutos, el recipiente se llenará con amebas. Se sabe que el contenedor contiene 220 amebas. ¿Cuántas amebas se colocaron inicialmente en el contenedor? Por favor programe.
Análisis: Según el significado de la pregunta, la ameba se divide cada 3 minutos, así que coloque la ameba en el recipiente al principio y llene el recipiente después de 45 minutos, lo que requiere 45/3 = 15 veces. . Pero "el contenedor puede contener hasta 2 ^ 20 amebas", es decir, el número de amebas obtenidas después de dividir 15 veces es 2 ^ 20. La pregunta requiere que calculemos el número de amebas antes de dividir.
También podríamos usar el método inverso para deducir el número antes de dividir por 15 (es decir, el número después de dividir por 14) de 2 ^ 20 después de dividir por 15, y luego deducir aún más el número después de dividir por 13, dividir por 12. ,... p>
Supongamos que el número antes de la primera división es x^0, el número después de la primera división es x^1, el número después de la segunda división es x^2,...15 división El número dividido es x 15, entonces tenemos
x 14 =x 15 /2, x 13 =x 14 /2,... x n-1 =x n /2 (n ≥ 1)< / p>
Debido a que se conoce el número después de la división 15, si la variable de iteración se define como X, la fórmula hacia atrás anterior se puede convertir en la siguiente fórmula de iteración:
X = X/2 (el valor inicial de X es el número 2^20 después de la decimoquinta división).
Repitiendo esta fórmula iterativa 15 veces, podrás calcular el número de amebas antes de dividirlas por 1. Debido a que el número requerido de iteraciones es un valor determinado, podemos usar un número fijo de bucles para controlar el proceso de iteración. El procedimiento de referencia es el siguiente:
cls
x=2^20
Para i=1 a 15
x=x /2
Siguiente I
Imprimir x
Fin
Ejemplo 3: Verificar la conjetura de Gujiao. El matemático japonés Tanigaki Seiichi descubrió un extraño fenómeno al estudiar los números naturales: para cualquier número natural n, si n es un número par, se divide por 2; si n es un número impar, se multiplica por 3 y luego se suma 1. Después de un número finito de operaciones, siempre se puede obtener el número natural 1. La gente llama a este descubrimiento de Tanaki Zuoshi la "Conjetura de Tanaki".
Requisito: escribir un programa para ingresar un número natural N a través del teclado y, después de un número finito de operaciones, imprimir el proceso completo para que N se convierta en un número natural 1.
Análisis: Defina la variable de iteración como n. Según el contenido de la conjetura de Gujiao, podemos obtener la relación de iteración en dos casos: cuando n es un número par, n = n/2; un número impar, n=n*3 1. La descripción en lenguaje QBASIC es:
Si n es un número par, entonces
n=n/2
Otros
n =n* 3 1
Terminará si...
Este es un proceso iterativo que requiere que la computadora lo repita. Cuántas veces es necesario repetir este proceso de iteración antes de que la variable de iteración n finalmente se convierta en un número natural de 1 es algo que no podemos calcular. Por lo tanto, es necesario determinar más a fondo las condiciones para finalizar el proceso iterativo. Después de analizar cuidadosamente los requisitos de la pregunta, no es difícil ver que para cualquier número natural n dado, se puede obtener el número natural 1 después de un número limitado de operaciones y se ha completado el trabajo de verificación. Por tanto, la condición para finalizar el proceso iterativo se puede definir como: n=1. El procedimiento de referencia es el siguiente:
cls
Ingrese "Por favor ingrese n="; n módulo 2 = 0, entonces
RemSi n es un número par, llame a la fórmula de iteración n=n/2.
n=n/2
Imprimir "";
Otro
n=n*3 1
Imprimir ""; n;
Terminará si...
Bucle
Fin
Método de iteración p>
El método iterativo es un método de diseño de algoritmos comúnmente utilizado para encontrar ecuaciones o raíces aproximadas de ecuaciones. Supongamos que la ecuación es f(x)=0, utilice algún método matemático para derivar la forma equivalente x=g(x) y luego siga los siguientes pasos:
(1) Seleccione una raíz aproximada de la ecuación y asignar Dar la variable x0;
(2) Guardar el valor de x0 en la variable x1, luego calcular g(x1) y guardar el resultado en la variable x0;
(3 ) Cuando el valor absoluto de la diferencia entre x0 y x1 sea menor que el requisito de precisión especificado, repita el cálculo en el paso (2).
Si la ecuación tiene raíces y la secuencia de raíces aproximada calculada con el método anterior converge, entonces x0 calculado con el método anterior se considera la raíz de la ecuación.
El algoritmo anterior se expresa en forma de programa C de la siguiente manera:
Algoritmo iterativo para encontrar las raíces de ecuaciones
{x0=raíz aproximada inicial;
Hacer {
x 1 = x0;
x0 = g(x 1);/*Calcular nuevas raíces aproximadas según ecuaciones específicas*/
} while ( fabs(x0- x1)>ε);
Printf("La raíz aproximada de la ecuación es f\n ", x0);
}
Los algoritmos iterativos también se usan comúnmente para encontrar las raíces de la ecuación, de modo que
X=(x0, x1,…,xn-1)
Dejemos que la ecuación se convierta en:
xi=gi(X) ( I=0, 1, ..., n-1)
El algoritmo iterativo para encontrar las raíces de la ecuación se puede describir de la siguiente manera :
El algoritmo iterativo para encontrar las raíces de la ecuación
{ for(I = 0;i
X=raíz aproximada inicial;
Hacer {
for(I = 0;i
y = x;
for(I = 0;i
X = gi(X);
for(δ= 0.0, I = 0;i
if (fabs(y-x)>delta)delta = fabs(y-x);
} while(delta gt;ε);
for(I = 0; I
Printf("La raíz aproximada de la variable x[d] es f ", I, x);
printf(" \ n ");
}
Al utilizar el método iterativo para encontrar raíces, debes prestar atención a las siguientes dos situaciones posibles:
(1) Si la ecuación no tiene solución, la secuencia raíz aproximada obtenida por el algoritmo no será Convergencia, el proceso de iteración se convierte en un bucle infinito. Por lo tanto, antes de usar el. algoritmo iterativo, verifique si la ecuación tiene una solución y limite el número de iteraciones en el programa
(2) Aunque la ecuación tiene una solución, la fórmula de iteración no se selecciona correctamente o una raíz aproximada inicial irrazonable. la selección también puede provocar fallas en la iteración.
Recursión
La recursión es una herramienta poderosa para diseñar y describir algoritmos, por lo que a menudo se usa en la descripción de algoritmos complejos antes de introducir otros algoritmos. métodos de diseño, debemos discutirlo primero.
Los algoritmos que se pueden describir de forma recursiva generalmente tienen las siguientes características: Para resolver un problema de tamaño n, intente descomponerlo en problemas más pequeños, y luego se resuelve. conveniente. Las soluciones de problemas más grandes se pueden construir a partir de las soluciones de estos problemas más pequeños. Estos problemas más pequeños también se pueden descomponer en problemas más pequeños usando el mismo método de descomposición y síntesis, y las soluciones de problemas más grandes se pueden construir a partir de las soluciones de estos. problemas más pequeños. En particular, cuando la escala N = 1, la solución se puede obtener directamente.
El problema consiste en escribir y calcular la enésima función fib(n) de la secuencia de Fibonacci.
La secuencia de Fibonacci es: 0, 1, 1, 2, 3,..., es decir:
fib(0)= 0; fib(1)= 1;
Fib(n)=fib(n-1) fib(n-2) (cuando n >;1).
Escrito como funciones recursivas incluir:
Fibra intermedia (fibra intermedia)
{ si (n==0) devuelve 0;
si (n==1) devuelve 1;
Si (n gt1) devuelve fibra (n-1) fibra (n-2);
}
El proceso de implementación del algoritmo recursivo se divide en Hay dos etapas: recursividad y regresión. En la fase recursiva, la solución de un problema más complejo (tamaño n) se traslada a la solución de un problema más simple (tamaño menor que n) que el problema original. Por ejemplo, en el ejemplo anterior, resuelva para fib(n) y obtenga fib(n-1) y fib(n-2).
Es decir, para calcular fib(n-1) y fib(n-2), primero debes calcular fib(n-1) y fib(n-2), y primero debes calcular fib(n- 3) y fib(n -4). Y así sucesivamente hasta que se calculen fib(1) y fib(0), y los resultados de 1 y 0 respectivamente estén disponibles de inmediato. Durante la fase recursiva, debe haber una situación que ponga fin a la recursividad. Por ejemplo, en la función fib, cuando n es 1 y 0.
En la etapa de regresión, cuando se obtiene la solución al caso más simple, se regresa paso a paso para obtener la solución a un problema un poco más complejo, por ejemplo, luego de obtener fib(1) y fib(. 0), vuelve a obtener fib(El resultado de 2),..., después de obtener fib(n-1) y fib(n-2).
Al escribir una función recursiva, tenga en cuenta que el conocimiento de las variables y parámetros locales en la función se limita a la capa de llamada actual. Cuando se envía a la capa de "problema simple", los parámetros y variables locales en la capa original se ocultan. En una serie de capas de "problemas simples", cada una de las cuales tiene sus propios parámetros y variables locales.
Debido a que la recursividad provoca una serie de llamadas a funciones y puede tener una serie de cálculos repetidos, la eficiencia de ejecución de los algoritmos recursivos es relativamente baja. Los programas suelen escribirse basándose en algoritmos recursivos cuando se pueden convertir fácilmente en algoritmos recursivos. Por ejemplo, en el ejemplo anterior, se utiliza un algoritmo recursivo para calcular la función fib (n) del enésimo elemento de la secuencia de Fibonacci, es decir, comenzando desde los dos primeros elementos de la serie de Fibonacci, comenzando desde los dos primeros elementos , calculando el siguiente elemento uno por uno hasta que el cálculo sea El enésimo elemento requerido.
Problema de combinación del problema
Descripción del problema: Encuentra todas las combinaciones de números R de los números naturales 1, 2,... Por ejemplo, todas las combinaciones de n=5 y r=3 son (1) 5, 4, 3 (2) 5, 4, 2 (3) 5, 4 y 1.
(4)5,3,2 (5)5,3,1 (6)5,2,1
(7)4,3,2 (8)4 , 3, 1 (9) 4, 2, 1
(10) 3, 2, 1
Al analizar las 10 combinaciones enumeradas, podemos usar este recursivo Piensa en el algoritmo para encontrar la función de combinación. Deje que la función void comb (int m, int k) se use para seleccionar entre los números naturales 1, 2,... Cuando se selecciona el primer número de la combinación, los siguientes números son k-1 de los números m-1 restantes. combinación de números. Esto transforma el problema de combinación de encontrar el número k a partir del número m en el problema de combinación de encontrar el número k-1 a partir del número m-1. Supongamos que la función introduce la matriz de trabajo a[] para almacenar el número resuelto de combinaciones, y se acuerda que la función colocará el primer número de las k combinaciones de números determinadas en a[k]. Al resolver una combinación, genere una combinación en []. El primer número puede ser m, m-1,... Después de que la función coloca el primer número de la combinación determinada en la matriz, hay dos opciones posibles. Debido a que los elementos restantes de la combinación no se han eliminado, se determinará de forma recursiva. O bien, como se han determinado todos los elementos de la combinación, se emite la combinación. Consulte la función peine en el programa a continuación para obtener más detalles.
Programa
#incluye
#define MAXN 100
int a[MAXN];
peine vacío (entero m, entero k)
{ int i, j;
for (I = m; i gt = k; i -)
{ a [k]= I;
if(k gt;1)
comb (i-1, k-1);
Otros
{ for(j = a[0]; j gt0; j -)
printf("4d ", a[j]);
printf(" \ n ");
}
}
}
void main()
{ a[0] = 3;
Comb(5,3);
}
Problema de mochila
Descripción del problema: Hay n valores diferentes y Para los artículos ponderados, encuentre un plan de selección para algunos de los n artículos de modo que el peso total de los artículos seleccionados no exceda el peso límite especificado, pero la suma de los valores de los artículos seleccionados sea la mayor.
Supongamos que los pesos de n elementos son w0, w1,..., wn-1, y los valores de los elementos son v0, v1,..., vn-1 respectivamente. Un esquema de selección que utiliza términos de búsqueda recursivos. Supongamos que hay muchas opciones alternativas, la opción con el valor total más grande se almacena en la opción de matriz [] y el valor total de esta opción se almacena en la variable maxv. Actualmente se está investigando una nueva solución y sus selecciones de proyectos se almacenan en la matriz cop[]. Supongamos que el plan actual ha considerado el punto i-1, y ahora se debe considerar el punto I, la suma de los pesos de los proyectos ya incluidos en el plan actual es TW, si se pueden seleccionar otros proyectos, ¿cuáles pueden ser? Lo que se logra con este plan El valor esperado del valor total es la televisión. Cuando se introduce tv en el algoritmo, una vez que el valor esperado del valor total del plan actual es menor que el valor total del plan anterior, deja de tener sentido continuar examinando el plan actual, por lo que el plan actual debe cancelarse y el siguiente plan debe ser examinado inmediatamente. Porque cuando el valor total de la solución no es mayor que maxv, la solución no se volverá a verificar, lo que también asegura que la solución encontrada después de la función será mejor que la solución anterior.
Existen dos posibilidades para la selección del ítem I:
(1) Considerando que se selecciona el primer ítem, esta posibilidad sólo es posible si el límite de peso total del plan es no excedido Es factible dadas las circunstancias. Una vez completada la selección, continúe considerando recursivamente la selección de otros elementos.
(2) Considere que el ítem I no está seleccionado. Esta posibilidad solo es posible cuando es posible encontrar una solución más valiosa sin el ítem I.
De acuerdo con la idea anterior. está escrito como un algoritmo recursivo de la siguiente manera:
Try (el primer elemento, la suma de los pesos logrados por la selección actual y el posible valor total tv de la solución)
{/*Considere la primera probabilidad de que un elemento se incluya en el esquema actual*/
Si (incluir el primer elemento es aceptable)
{Incluir el elemento I en el esquema actual;
Si (Yo
Intento (i 1, tw peso del artículo I, TV);
Otro
/*Otro completo solución, porque es mejor que la solución anterior y es la mejor solución*/
Guardar la solución actual como la mejor solución temporal;
Restaurar el estado no incluido en el proyecto I;
p>
}
/*Considere la posibilidad de que el primer artículo no esté incluido en el plan actual*/
Si (excluyendo el primer artículo, solo se pueden considerar hombres) p>
If (i
Try(i 1, tw, tv - el valor del artículo I);
Otro
/*Otro La solución completa, porque es mejor que la solución anterior y es la mejor solución*/
Guarde la solución actual como la mejor solución temporal;
}
Para comprender el algoritmo anterior, se proporcionan varios ejemplos a continuación.
Hay cuatro artículos, y sus pesos y valores son los que se muestran en la tabla:
Artículo 0 1 2 3
Peso 5 3 2 1
Valor 4 4 3 1
Y establezca el peso límite en 7. Luego, de acuerdo con el algoritmo anterior, la siguiente figura es el proceso de solución. Como puede verse en la figura, una vez que se encuentra una solución, el algoritmo procede a encontrar una mejor. Si se puede determinar que una rama de búsqueda no encontrará una mejor solución, el algoritmo no continúa buscando en esa rama sino que inmediatamente termina la rama e investiga la siguiente rama.
Las funciones y programas escritos según el algoritmo anterior son los siguientes:
Programa
#Incluye
#Definición número 100
Doble límiteW, totV, maxV
int opción[N], COP[N];
estructura {doble peso;
Doble valor;
} a[N];
int n;
búsqueda nula (int i, doble tw, doble tv)
{ int k;
/*Considere la posibilidad de que el primer artículo esté incluido en el plan actual*/
if(tw a . peso lt;=limitW)
{ COP = 1;
if(i
otro
{ for(k = 0;k
opción [k]= COP [k];
maxv = TV;
}
COP = 0
}
/ *Considerar la posibilidad de que el primer ítem no esté incluido en el esquema actual*/
if (TV-a . valor gt; maxV)
if (i
Otro
{ for(k = 0;k
opción[k]= COP[k];
maxv = TV -a . valor;
}
}
void main()
{ int
Double w, v;
Printf("Ingrese el número de elementos \ n "
scanf(("d ", ampn);
Printf ("Ingrese el peso de cada elemento y el valor \ n ");
for (totv=0.0, k = 0; k
{ scanf("1f1f ", ampw amp; cinco);
a[k]. peso = w;
a[k] valor = v
totV = V;
}
Printf("Ingrese el peso límite\ n ");
scanf("1f ", amplimitV);
maxv = 0.0 p>
for(k = 0;k find(0, 0.0, totV);
for(k = 0;k
if (opción[k]) printf ("4d ",k 1) ;
printf(" \ n el valor total es .2f \ n ", maxv
}
Para comparar; , usaremos la misma solución. Piense en soluciones de programas no recursivos. Para aumentar la velocidad de búsqueda de soluciones, el programa no simplemente genera todas las soluciones candidatas una por una, sino que forma una solución candidata que merece una mayor consideración a partir del impacto de cada elemento en la solución candidata. Al examinar cada elemento por turno, se forman soluciones candidatas.
Hay varias situaciones para examinar el primer ítem: cuando el ítem está incluido en el programa candidato y aún cumple con el límite de peso total del programa, se debe continuar considerando si está incluido en el programa candidato, por el contrario; El proyecto no debe incluirse en el programa candidato que se está formando actualmente en solución. De manera similar, un elemento se considerará no incluido en la solución candidata solo cuando no esté incluido en la solución candidata y sea posible encontrar una solución candidata mejor que la solución óptima temporal actual; por otro lado, el elemento no está incluido; Las opciones dentro del grupo de candidatos actual no deberían considerarse más a fondo. Para cualquier opción que merezca una mayor consideración, el programa procederá a considerar el siguiente punto.
Programa
#Incluye
#Definición número 100
Doble restricción w;
int COP[N ];
Elemento estructural {doble peso;
Valor doble
} a[N];
estructura { int
Doble tw;
Doble TV
} twv[N]; Siguiente void(int i, double tw, double tv)
{ twv. =1;
twv.tw = tw
twv.tv = tv
}
Búsqueda doble (struct ele *a, int n)
{ int i, k, f
Doble maxv, tw, tv, totv
maxv = 0; >for (totv=0.0, k = 0; k
totv =a[k]. valor;
Siguiente (0, 0.0, totv);
I = 0;
mientras(I gt;=0)
{ f=twv;
tw = twv.tw;
TV = twv . TV;
Interruptor(f)
{Caso 1: twv;
if(tw a . peso lt; =limitW)
if (i
{siguiente(i 1, tw a.peso, TV);
i;
} p>
Otros
{ maxv = tv
for(k = 0; k
cop[k]=twv[k ]. ! = 0;
}
Interrupción;
Caso 0: I-;
Interrupción;
Predeterminado: twv.=0;
if (TV-a . valor gt; maxv)
if (i
{ next(i 1, tw, TV- a . valor);
i;
}
Otros
{ maxv = TV-a .
for(k = 0;k
poli[k]=twv[k].
! =0;
}
Romper;
}
}
Devolver maxv
}
void main()
{ double maxv
Printf("Ingrese el número de elementos\ n ");
scanf (("d", ampn);
Printf("Ingrese el peso límite\ n ");
scanf("1f ", amplimitW);
Printf("Ingrese el peso y valor de cada artículo\ n ");
for(k = 0; k
scanf("1f1f ", ampa[k]. Peso ampa [valor k].
maxv=find(a,n);
printf("\nEl elemento seleccionado es\n");
for(k = 0;k
if (opción[k]) printf("4d ",k 1);
printf("\nEl valor total es .2f \ n ", maxv);
}
Conceptos básicos y características de la recursividad
La técnica de programación en la que un programa se llama a sí mismo se denomina recursividad.
p>
Un procedimiento o función llama a sus propios métodos directa o indirectamente en su definición o descripción. Generalmente transforma un problema grande y complejo en un problema pequeño similar al problema original. La estrategia recursiva puede resolver el problema con muy pocos. descripciones del programa. Los cálculos repetidos requeridos en el proceso reducen en gran medida la cantidad de código en el programa. La capacidad de recursividad radica en definir colecciones ilimitadas de objetos utilizando ideas recursivas.
En otras palabras, la recursividad requiere límites. condiciones, un segmento de avance recursivo y un segmento de retorno recursivo Cuando no se cumplen las condiciones de límite, la recursividad avanza cuando se cumplen las condiciones de límite, regresa recursivamente. >(1) La recursión se llama a sí misma en un procedimiento o función;
(2) Cuando se utiliza la estrategia de reducción incremental, debe haber una condición de fin de recursión clara, que se denomina salida recursiva
.