Repaso¶
Para el curso se deben presentar los conceptos de:
-
Programación orientada a objetos
- Clase: Es una plantilla o modelo para crear objetos que define atributos y métodos
- Objeto: Es una instancia de una clase, con valores específicos asignados a sus atributos
- Relaciones entre clases: Herencia, uso (asociación) y composición (agregación)
- Polimorfismo: Es cuando un método puede tener diferente comportamiento según el tipo del objeto que lo invoca, generalmente mediante herencia y sobrescritura de métodos
-
Lenguajes de programación:
- Tipos primitivos: int, bool, char, long, short, byte, double, float
- Tipos compuestos: string, list, array, vector, map, etc.
- Abstracciones (clases envolventes): Integer, Long, Double, etc. - objetos que encapsulan tipos primitivos y proporcionan métodos adicionales
-
Estructuras de control
- Condicional: if, if-else, if-else if
- Iterativas: for, while, do-while (aunque en este contexto se menciona que no se usarán)
- Switch/case: estructura de selección múltiple (se menciona que no se usará en este contexto)
-
Variables y valores: declaración de tipos y asignación de valores
Conceptos teóricos adicionales¶
Programación Orientada a Objetos (POO): Paradigma de programación que organiza el código en "objetos" que contienen datos (atributos) y comportamientos (métodos). Los cuatro pilares fundamentales son: encapsulación, abstracción, herencia y polimorfismo.
Recursión: Técnica donde una función se llama a sí misma para resolver un problema dividiéndolo en subproblemas más pequeños. Requiere un caso base que detenga la recursión.
Algoritmos de verificación de números primos: Un número primo es aquel que solo es divisible por 1 y por sí mismo. La optimización común es verificar divisores solo hasta la raíz cuadrada del número.
Complejidad algorítmica: Consideración importante al diseñar algoritmos, especialmente para funciones recursivas como el factorial, que tiene complejidad O(n).
Ejemplo¶
-
Deseo generar un arreglo de tipo
longque contenga los factoriales entre 0 y n. Esta función recibe como argumento a n. -
Deseo generar un arreglo de tipo
intque tenga los números primos entre n y m, donde n ≤ m, usando recursión.
import java.util.Arrays;
import java.util.Vector;
public class Cosita{
// Método recursivo para calcular el factorial de un número
// Caso base: factorial(0) = 1
// Caso recursivo: factorial(n) = n * factorial(n-1)
public long factorial(int n){
if (n == 0) {
return 1L; // Caso base: factorial de 0 es 1
}
else{
return n * factorial(n-1); // Llamada recursiva
}
}
// Método que genera un arreglo con los factoriales desde 0 hasta n
public long[] listaFactoriales(int n){
long sal[] = new long[n+1]; // Crear arreglo de tamaño n+1 (incluye factorial de 0)
for(int i = 0; i < sal.length; i++){
sal[i] = factorial(i); // Calcular factorial para cada índice
}
return sal;
}
// Método para verificar si un número es primo
public boolean esPrimo(int n){
boolean divisores = false; // Bandera para indicar si se encontraron divisores
// Optimización: solo verificar divisores hasta la raíz cuadrada de n
for(int i = 2; i <= Math.ceil(Math.sqrt(n)); i++){
if (n % i == 0) { // Si n es divisible por i
divisores = true; // Se encontró un divisor
break; // Salir del bucle temprano
}
}
// Operador ternario: si n es 2, retorna true, sino retorna el negativo de divisores
return (n == 2) ? true : !divisores;
}
// Método que genera una lista de números primos entre n y m (inclusive)
public Vector<Integer> listaPrimos(int n, int m){
Vector<Integer> sal = new Vector<Integer>(); // Crear Vector para almacenar resultados
// Iterar desde n hasta m (inclusive)
for(int i = n; i <= m; i++){
if(esPrimo(i)){ // Verificar si el número actual es primo
sal.add(i); // Agregar a la lista si es primo
}
}
return sal;
}
// Método principal para probar la funcionalidad
public static void main(String[] args) {
Cosita objCosita = new Cosita(); // Crear instancia de la clase
// Prueba 1: Lista de factoriales
System.out.println(Arrays.toString(objCosita.listaFactoriales(10)));
// Prueba 2: Verificación de números primos individuales
System.out.println(objCosita.esPrimo(10)); // false (10 no es primo)
System.out.println(objCosita.esPrimo(97)); // true (97 es primo)
System.out.println(objCosita.esPrimo(13)); // true (13 es primo)
System.out.println(objCosita.esPrimo(11)); // true (11 es primo)
// Prueba 3: Lista de primos en diferentes rangos
System.out.println(objCosita.listaPrimos(1000, 2000)); // Primos entre 1000 y 2000
System.out.println(objCosita.listaPrimos(2, 100)); // Primos entre 2 y 100
}
}
Tabla de resumen de conceptos¶
| Concepto | Descripción | Ejemplo en código |
|---|---|---|
| Clase | Plantilla para crear objetos que define atributos y métodos | public class Cosita |
| Objeto | Instancia de una clase con valores específicos | Cosita objCosita = new Cosita() |
| Herencia | Relación "es-un" entre clases (no mostrada en ejemplo) | - |
| Polimorfismo | Mismo método con diferente comportamiento según el objeto | - |
| Tipos primitivos | Tipos de datos básicos del lenguaje | int, long, boolean |
| Tipos compuestos | Estructuras que agrupan múltiples valores | long[], Vector<Integer> |
| Recursión | Función que se llama a sí misma | factorial() se llama recursivamente |
| Estructura condicional | Ejecuta código según una condición | if (n == 0) |
| Estructura iterativa | Repite código mientras se cumpla condición | for(int i = 0; i < sal.length; i++) |
| Método | Función definida dentro de una clase | public long factorial(int n) |
| Arreglo (Array) | Colección de elementos del mismo tipo | long sal[] = new long[n+1] |
| Vector | Colección dinámica similar a ArrayList | Vector<Integer> sal = new Vector<Integer>() |
Comentarios adicionales¶
-
Optimización del algoritmo de números primos: El método
esPrimoestá correctamente optimizado al verificar divisores solo hasta la raíz cuadrada del número, lo que reduce significativamente la complejidad computacional de O(n) a O(√n). -
Consideraciones sobre recursión: La función
factorialutiliza recursión, lo cual es elegante pero puede causar desbordamiento de pila (StackOverflowError) para valores grandes de n. Una implementación iterativa sería más eficiente en memoria. -
Uso de Vector vs ArrayList: En el código se utiliza
Vector, que es una clase sincronizada (thread-safe) de Java. Para aplicaciones de un solo hilo,ArrayListsería más eficiente.Vectores una clase legacy que se mantiene por compatibilidad. -
Manejo de números grandes: El factorial crece muy rápidamente, por lo que incluso con tipo
longse producirá desbordamiento para n > 20. Para valores mayores, se necesitaría usarBigInteger. -
Caso especial en verificación de primos: El método
esPrimomaneja correctamente el caso de n=2, pero podría tener problemas con números menores que 2 (0, 1 y números negativos), los cuales no son primos por definición. -
Eficiencia en
listaPrimos: El método podría optimizarse further implementando la criba de Eratóstenes para rangos grandes, especialmente cuando se buscan primos en intervalos extensos como entre 1000 y 2000. -
Buenas prácticas de programación: El código muestra buena estructuración con métodos específicos para tareas concretas, lo que facilita la reutilización y el mantenimiento.