![]() |
<<
Cuadrado lleno de |
| Dificultad: Se necesitan solamente conocimientos elementales de matemática. Algunos de los temas de computacion se pueden repasar leyendo las lecciones anteriores. Lecciones anteriores:
|
|
| Resumen: Vamos a analizar un problema y comparar varias formas de resolverlo. En esta lección mostramos la solución más sencilla que es justamente la más lenta. El problema original: En la Segunda Ronda del Torneo del año 2000 tomamos un problema en tercer nivel que decía: Se tiene un tablero de 4x4 en el que se anota un dígito en cada casilla. En cada fila y en cada columna se forma un número de cuatro dígitos. Un tablero de este tipo es legal si los ocho números que se forman así son múltiplos de 17. ¿Cuántos tableros legales distintos hay? Resulto bastante difícil, así que la idea es discutir un poco algunas maneras de hacer que valla más rápido y la solución termine en un tiempo razonable. Primera Propuesta: Vamos a empezar con la versión más simple del programa y analizar algunas variaciones. El programa prueba con todas los posibles números en las primeras tres filas y trata de completar cada columna para que sea un múltiplo de 17. Si lo logra se fija si la última fila también es múltiplo de 17 y en este caso aumenta el contador El programa separa los números en cifras utilizando una función. Llama a otra función para que busque cuál debería ser la ultima cifra de cada columna a medida que lo necesita. Para todas las cuentas usamos división(\) entera y módulo (Mod) que andan más rápido. El programa en QB es: Dim L1 As Long
Dim L2 As Long
Dim L3 As Long
Dim C1 As Long
Dim C2 As Long
Dim C3 As Long
Dim C4 As Long
Dim Columna As Long
Dim Contador As Long
Dim Inicio As Single
Dim Tiempo As Single
Dim Pasos As Single
Inicio = Timer
Print
Print "Tiempo", "Tiempo", "Tableros", "Valor"
Print "Trans.(s)", "Estimado(H)", "Encontrados", "L1"
'L1, L2, L3 son las tres primeras filas
For L1 = 0 To 9999
If L1 Mod 17 = 0 Then
For L2 = 0 To 9999
If L2 Mod 17 = 0 Then
Tiempo = Timer - Inicio
Pasos = L1 * 10000 + L2 + 0.1 ' "+.1" porque sino empieza de Cero
Print Tiempo, Tiempo / Pasos * 10000 ^ 2 / 60 ^ 2, Contador, L1
For L3 = 0 To 9999
If L3 Mod 17 = 0 Then
'En C1, C2, C3, C4 se guarda la ultima cifra de cada columna
C1 = CompletarNumero(Cifra(L1, 1), Cifra(L2, 1), Cifra(L3, 1))
C2 = CompletarNumero(Cifra(L1, 2), Cifra(L2, 2), Cifra(L3, 2))
C3 = CompletarNumero(Cifra(L1, 3), Cifra(L2, 3), Cifra(L3, 3))
C4 = CompletarNumero(Cifra(L1, 4), Cifra(L2, 4), Cifra(L3, 4))
If C1 >= 0 And C2 >= 0 And C3 >= 0 And C4 >= 0 Then
If C4 = CompletarNumero(C1, C2, C3) Then
Contador = Contador + 1
End If
End If
End If
Next
End If
Next
End If
Next
Print Contador
Function Cifra&(Numero As Long, Posicion As Long) ' As Long
Dim Columna As Long
Dim Queda As Long
Queda = Numero
For Columna = 4 To Posicion + 1 Step -1'Cuenta para atras
Queda = Queda \ 10
Next Columna
Cifra = Queda Mod 10
'en C: Return Cifra
End Function
Function CifraString&(Numero As Long, Posicion As Long) ' As Long
CifraString = Val(Mid$(Str$(10000 + Numero), Posicion + 2, 1))
'en C: Return Cifra
End Function
Function CompletarNumero&(N1 As Long, N2 As Long, N3 As Long) 'As Long
Dim Valor As Long
Dim Sobra As Long
Dim Falta As Long
Valor = N1 * 1000 + N2 * 100 + N3 * 10
Sobra = Valor Mod 17
If Sobra <> 0 Then
Falta = 17 - Sobra
If Falta >= 10 Then
Falta = -1 'ERROR
End If
Else
Falta = 0
End If
CompletarNumero = Falta
'Return Falta
End Function
Function CompletarNumeroBuscando&(N1 As Long,N2 As Long,N3 As Long)'As Long
Dim Valor As Long
Dim Ultimo As Long
Valor = N1 * 1000 + N2 * 100 + N3 * 10
For Ultimo = 0 To 9
If (Valor + Ultimo) Mod 17 = 0 Then
Exit For
End If
Next
If Ultimo >= 10 Then Ultimo = -1
CompletarNumeroBuscando = Ultimo
'Return Ultimo
End Function
(Nota: Quizás L1, L2, L3 y C1, C2, C3, C4 deberían ser dos vectores(array) ) |
|
| El
programa tiene dos funciones, cada una con dos versiones.
Además terminan en & para indicar que el resultado
es un Long (En el VB se puede indicar de una manera más
clara que es poniendo "As Long" al final.) La primera es Cifra(Numero As Long, Posicion As Long) que se usa para separar los números en cifras. Cuenta las posiciones de izquierda a derecha, suponiendo que el número tiene cuatro "cifras" y si es necesario completa con ceros. Hay otra función que aparece como CifraString que hace exactamente lo mismo, pero utilizando cadenas de texto. La otra función es CompletarNumero(N1 As Long, N2 As Long, N3 As Long) que dadas las primeras tres cifras de un número busca cuál es la cuarta para que sea un múltiplo de 17. Por ejemplo CompletarNumero(1,7,0)
da 0 porque 1700 es
múltiplo de 17. La función CompletarNumeroBuscando hace lo mismo, pero más a mano, probando con todos los números posibles. Comparando los tiempos: Además el programa va mostrando cuanto tiempo lleva ejecutándose y una estimación de cuánto va a tardar. A veces es bueno hacer estas estimaciones para no quedarse esperando que el programa termine o pulir durante media hora un programa que tarda 10 minutos. También la idea es que al practicar hagan varias versiones de las funciones para ver que tipo de cosas andan más rápido y acostumbrarse a usarlas. Al ejecutarlo utilizando las diversas funciones obtenemos los siguientes tiempos en una Pentium de ~100Mhz:
En realidad al principio (durante los primeros 2 o 3 minutos) el tiempo estimado que muestra el programa es mucho mayor (unas 17 veces :) ). Este pequeño misterio queda como ejercicio. Como se puede ver en la tabla la función para separar en cifras que usa cadenas es mucho más lenta y le agrega unas 10 horas más al programa, ¡¡de manera que tarda casi el doble!!. El efecto de usar CompletarNumeroBuscando no es tan terrible, pero son unas 2 horas más de ejecución. Una mejora importante se logra cambiando todos los "Long" por "Integer" (y los "& por "%"), bueno, todos menos 1 que hay que dejar como Long. Un error muy normal es confiarse de que ningún valor va a ser muy grande y usar Integer para ahorrar memoria y que el programa ande más rápido. Es importante darse cuenta a ojo cual es la única variable que no se puede achicar (otro ejercicio). (Ver Tabla de traducción de tipos de variables) Otra ayuda importante para que le programa vaya más rápido es el uso de la división entera en todos los casos en que es posible. Sería interesante cambiarlas por divisiones normales y ver cuanto (más) tarda. Más o menos carteles: También es importante regular correctamente la cantidad de carteles. Si aparecen más de unos 10 carteles por segundo el tiempo que le toma a la computadora hacer que la información en la pantalla suba empieza a molestar y a hacer realmente más lento el programa. Si entre cartel y cartel pasan más de unos 10 segundos uno se empieza a aburrir y preguntarse si se colgó. Yo prefiero unos 2 o 3 por segundo, pero es cuestión de gusto. Una forma de tener actualizado continuamente la pantalla sin que se consuma tanto tiempo es mediante la instrucción Locate (QB) o gotoxy (Pascal/C) que permite escribir siempre en un punto fijo de la pantalla. En la siguiente tabla se compara el tiempo que tarda el programa poniendo carteles adentro de diferentes ciclos. Por ejemplo si se pone un cartel en pantalla adentro de todo, de manera que se actualiza cada vez que la tercera fila cambia, el tiempo aumenta considerablemente de unas 10 horas a 6 días.
En las lecciones siguientes vamos a hacerle a este programa algunos pequeños cambios para que vaya más rápido. Ejercicios:
Lecciones siguientes: |
|
La idea es que hagan los ejercicios y piensen que otras cosas interesantes se pueden hacer relacionadas con estos temas. Cuéntennos lo que consiguieron y pregunten lo que no les salió. Envíen sus preguntas, dudas, sugerencias, experiencias y propuestas. Nuestra dirección es cym98@oma.org.ar .
También nos gustaría saber tu opinión sobre esta clase. Les pedimos que se tomen unos instantes y contesten estas preguntas. Con tu ayuda podremos hacer un curso cada vez mejor.
| OmaNet - Educación Interactiva www.oma.org.ar/omanet | omanet@oma.org.ar |
|
| mensajes: webmaster@oma.org.ar |