













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
En este documento se analiza el número de veces que se realizan los procesos en un bucle for() en Java, considerando diferentes casos de incremento y límite. Se calcula la complejidad de cada caso y se compara con el caso base. Se incluyen ejemplos de cómo modificar el límite y el incremento del bucle.
Tipo: Ejercicios
1 / 21
Esta página no es visible en la vista previa
¡No te pierdas las partes importantes!














Retroalimentación: Interrogantes de la clase anterior, el estudiante debe contestar estas preguntas con sus propias palabras y ejemplos, subirlos al aula virtual en el repositorio de actuaciones “Actuación de clase 4”:
Análisis, Tratamiento y Estrategias de Algoritmos
Los avances tecnológicos en la actualidad han tomado énfasis en el ilimitado recurso lógico, por lo que el estudio de la calidad de los algoritmos se enfocan en la administración de los recursos físicos que devuelvan resultados de forma rápida y confiable.
Recordemos que La tecnología tiene dos tipos de recursos: Físicos y Lógicos: Los recursos físicos , son aquellos componentes electrónicos, mecánico–electrónico y de comunicación que realizan una actividad que se manifiesta en mostrar u obtener un resultado o movimiento que el ser humano puede evidenciar. Los recursos lógicos , son las instrucciones que permiten el funcionamiento de los recursos físicos, estas instrucciones pueden ser eficientes o requieren de mejoras para optimizar su funcionamiento.
Análisis, Tratamiento y Estrategias de Algoritmos
Análisis de Algoritmos Es el estudio teórico de la^ eficiencia^ de un algoritmo.
for (int i=1; i<=5; i++){ System.out.println(“ ”+ i); } Definición de complejidad y su medida
Para explicarlo, se empezará analizando la estructura de un for() al desarrollar una serie de ciclos, estas acciones generarán una serie de valores como el número de asignaciones, el número de comparaciones y el número de cálculos o incrementos que se realizarán para cumplir su propósito, se deberá entender que estos procesos tendrán el mismo valor o peso. Por ejemplo, se desea mostrar del 1 al 5: for (int i=1; i<=5; i++){ System.out.println(“ ”+ i); }
1.- El contador tomará el valor inicial 2.- Se evaluará la condición de repetición, si el resultado es verdadero ingresará para realizar las instrucciones del ciclo. 3.- Aplicará las instrucciones del ciclo 4.- Finalizada las instrucciones del ciclo, realiza el incremento del contador 5.- Vuelve a realizar desde el segundo paso hasta que la condición sea falsa y saltará a la siguiente instrucción fuera del ciclo repetitivo Asignaciones Comparaciones Incrementos Resultado a mostrar Considerando esta mecánica del for(), analicemos el número de veces que se realizan los siguientes procesos: (^1 1 ) 2 3 4 5 6 2 3 4 5 1 2 3 4 5 Al analizar el proceso se podría indicar lo siguiente: (^) Se realizó 1 asignación (^) Se realizó 6 comparaciones, 1 más que el límite (^) Se realizó 5 incrementos En este ejemplo del for() el límite de ciclos es 5 pero tenga en cuenta que puede ser cualquier valor, por lo que, para encontrar el total de procesos en un formato calculable, este límite será reemplazado por la variable (n) quedando de la siguiente forma: = 1 asignación + (n + 1 comparaciones) + n incrementos “recuerde que n reemplaza al 5 de este ejemplo” = 1+n+1+n Esta expresión de calculo se la puede simplificar = 2n+2 Se puede expresar que 2n+2 es la complejidad del for()
for (int i=5; i>0; i--){ System.out.println(“ ”+ i); } Definición de complejidad y su medida Asignaciones Comparaciones Decrementos Resultado a mostrar Considerando la mecánica del for(), analicemos el número de veces que se realizan los procesos cuando se decrementa: (^1 1 ) 2 3 4 5 6 2 3 4 5 5 4 3 2 1 Al analizar el proceso se podría indicar lo siguiente: (^) Se realizó 1 asignación (^) Se realizó 6 comparaciones, 1 más que el límite (^) Se realizó 5 incrementos = 1 asignación + (n + 1 comparaciones) + n incrementos “recuerde que n reemplaza al 5 de este ejemplo” = 1+n+1+n Esta expresión de calculo se la puede simplificar = 2n+2 Se puede expresar que 2n+2 es la complejidad del for() for (int i=0; i<10; i+=2){ System.out.println(“ ”+ i); } Asignaciones Comparaciones Incrementos Resultado a mostrar Considerando la mecánica del for(), analicemos el número de veces que se realizan los procesos cuando el incremento es el doble: (^1 1 ) 2 3 4 5 6 2 3 4 5 0 2 4 6 8 Al analizar el proceso se podría indicar lo siguiente: (^) Se realizó 1 asignación (^) Se realizó 6 comparaciones, 1 más de la mitad = 1 asignación + (n/2 + 1 comparaciones) + n/2 incrementos^ ^ Se realizó^^5 incrementos, la mitad del límite = 1+n/2+1+n/2 Esta expresión de calculo se la puede simplificar = 2n/2+2 Esta expresión de calculo se la puede simplificar aún más 2 n/ 2 = n+2 Se puede expresar que n+2 es la complejidad del for() Este mismo resultado obtendríamos si modificamos la condición de repetición, por ejemplo:
1 asignación n/2+1 comparaciones n/2 incrementos = 1+n/2+1+n/ = 2n/2+ = n+ = 1 asignación + n/2 + 1 comparaciones+ n/2 incrementos
Definición de complejidad y su medida c=0; for (int i=1; i<=n; i++){ for (int k=1; k<=n; k++){ c++; } } Considerando la mecánica del for(), ahora analizaremos el número de veces que se realizan los procesos cuando se trabaja con dos for() anidados: Por los análisis realizado en las anteriores diapositivas se conoce que la complejidad de un for() esta expresado por 2n+2 considerando que el incremento es de 1 en 1, parte desde 1 y llega a n. Al analizar la complejidad de los for() anidados tenemos:
En esta instrucción hay 1 asignación de complejidad En este for() hay 2n + 2 de complejidad En este for() hay (2n + 2) * n de complejidad, esta multiplicación por n se da ya que por cada ciclo del for() externo controlado por i se realiza el for() interno controlado por k En esta instrucción hay nn* de complejidad, tenga en cuenta que se repite n veces por el for() interno controlado por k y n veces que este for() se repite por el for() externo controlado por i 1 asignación + 2n + 2 del primer for + (2n+2)n del segundo for + nn de incremento de c = 1 + 2n + 2 + (2n+2)n + nn = 3 + 2n + 2n^2 + 2n + n^2 = 3 n^2 + 4n + 3 Este es el resultado completo del proceso expresado de forma calculable, pero si se desea encontrar la complejidad computacional basado en el resultado del proceso (c++) se aplica una simplificación asíntota que omite las contantes y se mantiene el de mayor tasa de crecimiento 3 n^2 = n^2 4 n = n 3 = 0 = n^2 + n se toma el que tiene mayor tasa de crecimiento para expresar la complejidad T(c) = n^2 Se podría sustituir n^2 por 10^2 y obtendríamos 100 que es el número de veces que c se incrementaría
i=1; while (i < n) { i*=2; } Definición de complejidad y su medida Para el siguiente análisis se utilizará el control de repetición while() como instrumento de análisis y el caso de un crecimiento de control exponencial para obtener una expresión calculable de comparación (i < n), para verificar este proceso considere que n tiene un valor de 10. 1 2 4 8 16
Observe que el crecimiento es exponencial, por lo tanto los valores de i tendrían las siguientes expresiones: 20 21 22 23 24 La condición (i<n) por cada validación estaría estructurada de la siguiente forma (2^0 <n) (2^1 <n) (2^2 <n) (2^3 <n) (2^4 <n) (2x^ <n)
(2x)
Para trabajar con potencia se utilizan los logaritmos para generar este tipo de expresiones: 2 3 = 8 log 2 8 = 3 La base de la potencia se convierte en base del logaritmo El exponente se convierte en logaritmo (2x^ < n) se aplica logaritmo en ambos extremos de la comparación (log 2 2 x^ < log 2 n) (x < log 2 n) esta sería la complejidad de la condición del while() conforme al ejemplo (log 2 2 x^ < log 2 n) Se separa el exponente y se calcula log 22 el resultado es 1 21 = 2 , expresado así: x log 22 1 Si se desea mostrar la expresión matemática de todo el proceso sería así:
En esta instrucción hay 1 asignación de complejidad En esta instrucción hay log 2 n + 1 de comparaciones En esta instrucción hay log 2 n de multiplicaciones = 1 + log 2 n + 1 + log 2 n = 2 + log 2 n + log 2 n = 2 + 2log 2 n esta sería la complejidad del proceso
Definición de complejidad y su medida Algunas metodologías aplican mayores detalles para desarrollar los análisis de complejidad computacional, se basan en la diferenciación de tiempos que toman los diferentes proceso para cumplir con el propósito de la instrucción, para simplificar el cálculo, se hará una estimación de los costes (valores) de los algoritmos agrupando las operaciones realizadas en tres clases diferentes y asumiendo un coste temporal único para cada uno de ellos:
o
a
c t representa el total Alternativa 1. El siguiente proceso se basa en dos variables y un ciclo repetitivo : y = 0; i = 1; while (i <= 100) { y = y + x; i = i + 1; } 100 *(to+ta)
Alternativa 2. En realidad el problema se puede resolver fácilmente ya que sumar cien veces la variable x es análogo a multiplicar x por 100: y = 100 * x; (^) = 1 t o + 1^ ta Para probar el análisis de complejidad computacional mediante estos tipos de métricas, asuma que se desea generar un proceso que acumule en una variable la suma de un número 100 veces 1 ta 1 ta 100*tc + 1 tc (comparaciones del bucle) 100 *(to+ta)
Esta expresión se puede simplificar en una sola operación:
Definición de complejidad y su medida Ahora considere el mismo procedimiento de la diapositiva anterior pero utilizando n como límite de repetición: y = 0; i = 1; while (i <= n) { y = y + x; i = i + 1; } n(to+ta) 1 ta 1 ta ntc + 1 tc n*(to+ta)
Como era de esperar, el tiempo total que consumirá el algoritmo para obtener el resultado depende del valor n , que es una entrada del algoritmo. De manera que, el coste temporal del algoritmo será distinto para diferentes valores de n. El siguiente proceso se trata de una algoritmo que realiza una búsqueda secuencial, la función asume que los datos son de tipo entero y que, como resultado de la búsqueda, sólo interesa saber si elemento x está o no en el vector.
// v es el vector, n es el número de elementos en el vector, x es el dato buscado ntc 1 ta ntc + 1 tc n*(to+ta) 1 ta ¿? Observe que no sabemos cuantas veces se ejecutará esta asignación porque dependerá de la condición En este punto es donde aparece el concepto de mejor caso, peor caso y caso medio ya que dependerá del valor de entrada x (dato buscado) y de los elementos almacenados en el vector Se dice que un algoritmo tiene un mejor caso cuando los parámetros del problema lleven a realizar el menor número de pasos posibles, se representará por Tm Se dice que un algoritmo tiene un peor caso cuando los parámetros del problema lleven a realizar el máximo número de pasos posibles, se representará por Tp Por último, el caso medio (o coste esperado) de un algoritmo vendrá expresado por la media de pasos realizados en función de todos los casos posibles (básicamente se deben analizar todos los casos posibles teniendo en cuenta la probabilidad de que ocurra cada uno de esos casos), el tiempo medio se representará por Tμ^.
boolean BusquedaSecuencial (Vector v, int n, int x) { int i; i = 0; while ( ( i < n ) && ( v[i] != x ) ){ i = i + 1; } if ( i == n ) return false; else return true; } Mejor y Peor Caso ( BÚSQUEDA SECUENCIAL CON PARADA) El algoritmo anterior es susceptible de ser mejorado de diferentes maneras. Una mejora obvia consiste en finalizar la búsqueda en el momento en que se encuentre el elemento buscado, ya que entonces no tiene sentido seguir con la búsqueda en el resto del vector Con esta modificación, el bucle debe controlar dos condiciones, que representan los dos motivos por los que se puede finalizar la búsqueda: (^) Porque se ha encontrado el elemento o (^) Porque se ha alcanzado el final del vector sin encontrarlo. Esta condición tiene 3 operaciones de comparación 1era^ (i < n) , 2da^ (v[i] != x) y la 3era^ que es la unión de la primera y la segunda mediante && El mejor caso se da cuando el elemento x se encuentra en la primera posición del vector, por lo tanto T m = 1ta + 4tc El peor caso se dará cuando el elemento x no se encuentra en el vector y, por tanto, es preciso comprobar hasta el final todos sus elementos Al realizar la primera condición dará verdadero, la segunda dará falso porque se supone que el valor de la primera posición es el buscado, es decir, el valor de la posición v[0] es igual que el valor de x , observe que la condición verifica si son diferentes, así la respuesta es falso. La tercera operación lógica (verdadero && falso) la respuesta será falso, así que la complejidad computacional será: En este caso siempre se dará verdadero en todas las comparaciones de la condición del while a excepción de la última comparación, cuando i por efecto del incremento es igual que n, en este caso el lenguaje C, C#, Java y otros lenguajes de programación, cuando existe operaciones And (&&) si la primera es falso no evalúa las otras y el resultado será falso, así que la complejidad computacional será: Tp^ = 1 ta + n(tc + tc + tc) + 1 tc + n(to+ta) + 1 tc Tp**^ = ta + n (3tc) + nto + nta + 2 tc Tp^ = (ta + nta) + (3ntc + 2 tc) + nto Tp^ = ta(1+ n) + tc(3n + 2) + nto
boolean BusquedaSecuencial (Vector v, int n, int x) { int i; i = 0; v[n] = x; while ( v[i] != x ) { i = i + 1; } if ( i == n ) return false; else return true; } Mejor y Peor Caso (BÚSQUEDA SECUENCIAL CON CENTINELA) En el algoritmo anterior se plantean condiciones que comprueban que el índice permanezca en el rango adecuado (es decir, entre 0 y n-1) para que la búsqueda no continúe fuera de este rango. Como una estrategia de desarrollo se propone que, esta comprobación no sea necesaria si se pudiera asegurar que se va a encontrar el elemento buscado. Con esta técnica se puede estar seguro de encontrar el elemento, si se ubica “manualmente” en una posición estratégica del vector, por ejemplo, en la posición n (la última posición) a esta estrategia se le conoce con el nombre búsqueda con centinela El mejor caso se da cuando el elemento x se encuentra en la primera posición del vector, por lo tanto
Al evaluar la condición del while por primera vez dará falso, así que la complejidad computacional será: El peor caso se dará cuando el elemento x no se encuentra en el vector y, por tanto, es preciso comprobar hasta ubicarse en la posición n del centinela al final de todos los elementos Tp^ = 2 ta + n (tc) + n(to+ta) + 1 tc Tp*^ = 2 ta + ntc + nto + nta + tc Tp^ = (ta + nta) + (ntc + tc) + nto Tp^ = ta(1+ n) + tc(n + 1) + nto Notará que este algoritmo en el peor de los casos es más eficiente que el algoritmo anterior Peor Caso: Para vectores de tamaño n , las entradas que hacen que el algoritmo trabaje más son aquellas en que el valor buscado no se encuentra en el vector: Tpeor( n ) = n comparaciones Tpeor(n) = máx{ T(n, entrada) } para toda entrada de Tamaño n. Mejor Caso: Las entradas que hacen que el algoritmo trabaje menos son aquellas en que el valor buscado está en la primera posición del vector. Tmejor( n ) = 1 comparación Tmejor(n) = min{ T(n, entrada) }
public int[] arrayt(int[] A) { int []B; int i, total=0; for(i=0; i<A.length; i++){ B[i]=A[i]; total+=A[i]; } for (i = 0; i< A.length; i++){ B[i] = total - A[i]; } return B; } Desarrollo de un problema Dado un arreglo A de longitud n, genere un nuevo arreglo, también de longitud n, tal que el i -ésimo elemento contenga el resultado de sumar todos los elementos de la lista excepto él mismo. Cumpliendo con el requerimiento del enunciado obtendríamos el siguiente vector resultado B: [66, 63, 53, 55, 66, 55, 51, 67] Entrada del vector A: [2, 5, 15, 13, 2, 13, 17, 1] Si realizamos la suma de los elementos de este vector expresado en función de la operación tendríamos: sum(A) = 68 2n+2 complejidad del for 1 asignación n número de veces que se ejecutará = 1 + 2n + 2 + n + n + 2n + 2 + n = 7n + 5
Considere el siguiente proceso de eficiencia: El resultado se lo puede expresar de forma algebraica con la función lineal: n número de veces que se ejecutará 2n+2 complejidad del for n número de veces que se ejecutará
Los algoritmos existen para resolver un problema particular de manera eficiente pero ¿qué significa que un algoritmo sea eficiente? o ¿bajo qué métrica podemos concluir que una solución es más eficiente que otra? o predecir la cantidad de recursos que éste ocupará durante su ejecución, como ¿cuánta memoria RAM? ¿qué tanto ancho de banda? o incluso ¿qué otras partes del hardware utilizará y en qué medida? , responder éstas preguntas es el campo de estudio del análisis de algoritmos , considere que el interés estará basado en predecir en vez de obtener resultados exactos. En computación, la notación asintótica nos permite representar la complejidad, y por ende la eficiencia, de un algoritmo, de tal manera que podemos proyectar el aumento de operaciones requeridas al aumentar el tamaño de la entrada, es decir, cuando n crece o tiende al infinito.