![]() |
<< Llenando el tablero en otros ordenes>> |
Lecciones anteriores:
Memorizando Respuestas (Algunas mejoras importantes al
programa original)
El problema original:
En la primera lección de esta serie analizamos el siguiente problema:
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?
Probamos diferentes formas de hacer un programa que lo resuelva, para poder comparar algunos métodos, pero todos los programas tardaban más de 5 horas en una Pentium ~100 Mhz. En la segunda lección vimos algunas mejoras que llevaban el tiempo a media hora. Ahora vamos a tratar de mejorar esto cambiando la forma en que elegimos los números.
Llenando el tablero
Para poder hablar cómodamente de los casilleros vamos nombrarlos como en la batalla naval, o sea a las columnas les asignamos letras y a las filas números. La misma notación se usa en las variables del programa
| A1 | B1 | C1 | D1 |
| A2 | B2 | C2 | D2 |
| A3 | B3 | C3 | D3 |
| A4 | B4 | C4 | D4 |
En las lecciones anteriores no se van eligiendo las cifras una por una, sino que se eligen las primeras tres de cada fila al mismo tiempo, pero si uno lo piensa un poco es como si se fueran llenando en cada fila de izquierda a derecha, en general la primera esta fija pero la tercera varia muy rápidamente. Dibujándolo queda el siguiente orden de llenado.
| 1 | 2 | 3 | 4 |
| 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 |
Lo que tiene de malo es que al llenar el noveno casillero(A3), puede ser que no haya ninguna forma de llenar A4 de manera que la columna A tenga un múltiplo de 17. Pero eso recién lo vamos a ver en el 13avo paso de llenado después de probar todos los llenados posibles de B3, C3 y D3 que son completamente inútiles si no es posible llenar A4. Esto hace perder tiempo y se repite con las otras columnas. Así que sería mejor si cada vez que tenemos tres números de una fila buscamos completarla inmediatamente. Esto da el siguiente orden de llenado:
| 1 | 2 | 3 | 4 |
| 5 | 6 | 7 | 8 |
| 9 | 11 | 13 | 15 |
| 10 | 12 | 14 | 16 |
De todas maneras se puede ganar un poco de tiempo intercalando el llenado de una horizontal y una vertical. La diferencia es poca y no me queda muy claro el motivo (quizás tenga que ver con que al estar fijada ya la primera columna se tiene que llenar un cuadradito de 3x3 que es mas chico y así es necesario repetir los cálculos para D4 menos veces(?)). Esto corresponde al siguiente orden que es el que se usa en el programa.
| 1 | 2 | 3 | 4 |
| 5 | 8 | 9 | 10 |
| 6 | 11 | 13 | 14 |
| 7 | 12 | 15 | 16 |
El programa:
Aprovecha la idea de la lección pasada y guarda los resultados de CompletarNumero en una matriz. Sin embargo como llenamos las cifras en el tablero una por una no necesitamos la función Cifras.
Declare Sub IniciarMatrices ()
Declare Function CompletarNumero% (N1 As Integer, N2 As Integer, N3 As Integer) 'As Integer
Const C9999D17 = 9999 \ 17
Dim Shared MatCompletarNumero(0 To 9, 0 To 9, 0 To 9) As Integer
Dim A1 As Integer, A2 As Integer, A3 As Integer, A4 As Integer
Dim B1 As Integer, B2 As Integer, B3 As Integer, B4 As Integer
Dim C1 As Integer, C2 As Integer, C3 As Integer, C4 As Integer
Dim D1 As Integer, D2 As Integer, D3 As Integer, D4 As Integer
Dim Contador As Long
Dim Inicio As Single
Dim Tiempo As Single
Dim Pasos As Single
Inicio = Timer
IniciarMatrices
Print
Print "Tiempo", "Tiempo", "Tableros", "Valor"
Print "Trans.(s)", "Estimado(H)", "Encontrados", "L1"
For A1 = 0 To 9
For B1 = 0 To 9
For C1 = 0 To 9
D1 = MatCompletarNumero(A1, B1, C1)
If D1 >= 0 Then
Tiempo = Timer - Inicio
Pasos = A1 * 1000 + B1 * 100 + C1 * 10 + D1 + 0.1 ' "+.1" porque sino empieza de Cero
Print Tiempo, Tiempo / Pasos * 10000 / 3600, Contador, Pasos - 0.1
For A2 = 0 To 9
For A3 = 0 To 9
A4 = MatCompletarNumero(A1, A2, A3)
If A4 >= 0 Then
For B2 = 0 To 9
For C2 = 0 To 9
D2 = MatCompletarNumero(A2, B2, C2)
If D2 >= 0 Then
For B3 = 0 To 9
B4 = MatCompletarNumero(B1, B2, B3)
If B4 >= 0 Then
For C3 = 0 To 9
D3 = MatCompletarNumero(A3, B3, C3)
If D3 >= 0 Then
C4 = MatCompletarNumero(C1, C2, C3)
If C4 >= 0 Then
D4 = MatCompletarNumero(D1, D2, D3)
If D4 >= 0 Then
If D4 = MatCompletarNumero(A4, B4, C4) Then
Contador = Contador + 1
End If 'D4=D4'
End If 'D4
End If 'C4
End If 'D3
Next C3
End If 'B4
Next B3
End If 'D2
Next C2
Next B2
End If 'A4
Next A3
Next A2
End If 'D1
Next C1
Next B1
Next A1
Tiempo = Timer - Inicio
Pasos = L1 + 0.1 ' "+.1" porque sino empieza de Cero
Print Tiempo, Tiempo / 3600, Contador, "Fin"
Function CompletarNumero%(N1 As Integer, N2 As Integer, N3 As Integer) 'AS Integer
Dim Valor As Integer
Dim Sobra As Integer
Dim Falta As Integer
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
Sub IniciarMatrices()
Dim X1 As Integer
Dim X2 As Integer
Dim X3 As Integer
Dim X4 As Integer
For X1 = 0 To 9
For X2 = 0 To 9
For X3 = 0 To 9
MatCompletarNumero(X1, X2, X3) = CompletarNumero(X1, X2, X3)
Next X3
Next X2
Next X1
End Sub
La parte principal del programa tiene demasiados For e If anidados, por eso cada vez que cerramos uno dejamos indicado a cual corresponde para facilitar la búsqueda y corrección de errores.
(Quizás habría que cambiar A1, A2, ... ,D4 por una matriz de 4x4. Creo que sería un poquito más lento el programa. si alguno lo prueba nos puede mandar sus resultados.)
Comparando los tiempos:
Al correr los programas con cada orden de llenado obtenemos los valores de la siguiente tabla.
Condiciones |
Tiempo Total (Minutos) |
Primero llenamos la 3 primeras Filas, después las columnas |
19,95 |
Completamos la cuarta casilla de cada columna lo antes posibles |
7,12 |
Intercalando el llenado de filas y columnas |
7,02 |
Primero
llenamos la 3 primeras Filas, después las columnas |
87,22 |
Intercalando
el llenado de filas y columnas |
30,65 |
En las dos últimas filas comparamos los distintos ordenes de llenado cuando no utilizamos la idea de la lección pasada, o sea que cada vez que necesitamos el valor de CompletarNumero llamamos a la función para que lo calcule. En estos ejemplo se ve que el método que usamos en esta lección es independiente del método de la anterior, y puede haber casos en que se pueda usar uno pero no el otro. Sin embargo los mejores resultados se obtienen (obviamente) aprovechando ambos.
Hay algunos otros órdenes de llenado posibles, pero no creo que mejoren mucho el tiempo de ejecución. Si alguno tiene ganas de probar un poco nos interesaría conocer sus resultados. Acuérdense de incluir en sus pruebas el tercer orden propuesto para poder eliminar (o tratar de eliminar) las diferencias entre una computadora y otra.
Ejercicios:
Lecciones siguientes:
ninguna por ahora
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 |