Juegos de Azar
y
Programación Declarativa
Alejandro Pérez Roca
Daniel Martín Prieto
Jorge Tudela Glez de Riancho
Índice
•
•
•
•
Introducción.
Reglas de los juegos.
Recopilación y formateo de datos.
Programas de obtención de premios.
o Apuestas múltiples.
o Diferencias entre los tipos de juegos.
• Programas de búsqueda de patrones.
o Patrones numéricos.
o Patrones visuales.
• Resultados curiosos.
• Ventajas de utilizar un lenguaje declarativo.
Introducción
Introducción
• Nuestro objetivo consistía en conseguir implementar los
juegos del Euromillón, la Primitiva y la Quiniela basándonos
en la historia de dichos juegos, aprovechando las ventajas
tanto de eficiencia como de elegancia que nos proporciona
un lenguaje declarativo.
• Además, pretendíamos descubrir y analizar resultados
curiosos aplicando patrones para conocer hasta qué punto
es viable el uso de estos a la hora de apostar.
Reglas de los juegos
Reglas del Euromillón
• Juego de lotería europeo organizado por diferentes
instituciones de Loterías, entre ellas Loterías y Apuestas
del Estado.
• El sorteo tiene lugar los viernes y salvo excepción se juega
a las 21.30 en París.
• El juego consiste en elegir 5 números entre 1 y 50 y otros
dos números comprendidos entre el 1 y el 9, conocidos
como estrellas.
• Las apuestas múltiples consisten en marcar más de 5
números hasta un máximo de 10.
• No se pueden marcar más de dos estrellas.
Reglas del Euromillón
El precio de cada apuesta es de 2 €, destinándose el 50% de la
recaudación íntegra a los premios de la siguiente forma:
• 16.00% para premios de la Primera Categoría (5 números y
dos estrellas).
• 3.70% para premios de la Segunda Categoría (5 números y
una estrella).
• 1.05% para premios de la Tercera Categoría (5 números y
ninguna estrella).
• 0.75% para premios de la Cuarta Categoría (4 números y
dos estrellas).
• 0.50% para premios de la Quinta Categoría (4 números y
una estrella).
• ...
Reglas de la Primitiva
• Es un juego de azar cuyo primer sorteo data de 1985, se
celebran unos 100 sorteos al año y, hasta hoy, más de 2300.
• Consiste en elegir seis números diferentes entre el 1 y el
49.
• Se pueden realizar apuestas múltiples eligiendo 5,7,8,9,10 u
11 números.
• El sorteo se realiza extrayendo siete bolas al azar de un
bombo de 49 bolas numeradas del 1 al 49.
o Las seis primeras son la combinación ganadora.
o La séptima es el complementario.
• Por último se extrae el reintegro de un bombo de diez bolas
numeradas del 0 al 9.
Reglas de la Primitiva
El precio de cada apuesta es de 1 €, destinándose el 55% de la
recaudación íntegra a los premios de la siguiente forma:
• 52% para premios de la Primera Categoría (6 aciertos).
• 8% para premios de la Segunda Categoría (5 aciertos +
complementario).
• 16% para premios de la Tercera Categoría (5 aciertos).
• 24% para premios de la Cuarta Categoría (4 aciertos).
• 8 € para premios de la Quinta Categoría (3 aciertos).
• El reintegro es asignado aleatoriamente por el sistema a
cada boleto.
Reglas de la Quiniela
• Se basa en los resultados de la LFP.
• El juego consiste en el sistema 1X2, donde sólo se
pronostica si gana el equipo local (1), visitante (2), o si el
partido queda empate (X).
• La apuesta se realiza sobre una lista de 15 partidos, donde
'El Pleno al 15' sólo se tiene en cuenta en caso de haber
acertado los 14 restantes.
• Además de la apuesta sencilla, hay otras formas de apostar,
pudiendo seleccionar para un mismo partido dos resultados
posibles (doble) o incluso los tres (triple).
Reglas de la Quiniela
El precio de cada apuesta es de 0.50 €, destinándose el 55%
de la recaudación íntegra a los premios de la siguiente
forma:
• 10% para premios de la Categoría Especial (Primera
Categoría + Pleno al 15).
• 12% para premios de la Primera Categoría (14 aciertos)*.
• 8% para premios de la Segunda Categoría (13 aciertos)*.
• 8% para premios de la Tercera Categoría (12 aciertos)*.
• 8% para premios de la Cuarta Categoría (11 aciertos)*.
• 9% para premios de la Quinta Categoría (10 aciertos)*.
(*) Sin contar el Pleno al 15.
Recopilación
y
formateo de datos
Datos originales
• Los datos de historia de los tres juegos han sido recopilados
de www.onlae.es, la web oficial de Loterías y Apuestas del
Estado.
• Dichos datos, han sido copiados tal cual a un fichero de
texto plano.
Datos originales
• De esta forma quedaría el fichero original, el cual tenemos
que formatear porque hay información que no nos interesa:
Datos formateados
• Al fichero anterior, se le ha aplicado un filtro Lex para eliminar
información que no queremos y adaptar el formato a las listas de
Haskell, a través de las que se accederán a los datos para que los
programas realicen los cálculos.
o La primera lista es la apuesta ganadora.
o La segunda la forman ventas, bote y acertantes con sus premios
correspondientes.
Datos formateados
• Mediante la función readFile iremos accediendo a la
información de forma perezosa, según lo vayan
necesitando las funciones correspondientes, de una forma
simple y cómoda.
quiniela :: String -> IO ()
quiniela apuesta = do
x <- readFile historiaQuiniela
let ganancias = calcularPremioHistoria
((traducir . words) apuesta) (lines x)
...
...
Datos formateados
• Además, debido a que Haskell es un lenguaje de
programación fuertemente tipado y para que haya
coherencia de tipos, utilizamos la función read, que
convierte un String al tipo inferido.
calcularPremioHistoria :: [Int] -> [String] -> Float
calcularPremioHistoria _ [] = 0.0
calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) =
calcularPremioDía apuesta (read ganadora) (read premios)
+ calcularPremioHistoria apuesta restoHistoria
Programas de obtención
de premios
Programas de obtención de premios
• En esta sección se van a mostrar las funciones necesarias
para implementar un programa que:
o
o
Reciba como entrada una combinación.
Nos devuelva las ganancias y pérdidas que obtendríamos
si hubiésemos jugado con esta combinación durante toda
la historia de la lotería.
Código del Euromillón
• Lectura de ficheros y llamada al programa de cómputo.
euromillón :: [Int] -> IO ()
euromillón apuesta = do
x <- readFile historiaEuromillón
let ganancias = calcularPremioHistoria apuesta (lines x)
let pérdidas = fromIntegral ((length . lines) x)/2 * calcularPrecioApuesta apuesta
print (ganancias, pérdidas, ganancias - pérdidas)
Código del Euromillón
• Cálculo del precio de una apuesta. Apuestas múltiples.
calcularPrecioApuesta :: [Int] -> Float
calcularPrecioApuesta apuesta =
2.0 * fromIntegral (comb ((length apuesta)-2) 5)
• Cálculo de las ganancias generadas por una apuesta
durante toda la historia.
calcularPremioHistoria :: [Int] -> [String] -> Float
calcularPremioHistoria _ [] = 0.0
calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) =
calcularPremioDía apuesta (read ganadora) (read premios)
+ calcularPremioHistoria apuesta restoHistoria
Código del Euromillón
• Cálculo de las ganancias generadas por una apuesta en un
día.
calcularPremioDía :: [Int] -> [Int] -> [Float] -> Float
calcularPremioDía apuesta ganadora premios =
calcularPremioCategoría (categoríaPremio apuesta ganadora) premios
• Obtención de la categoría del acierto.
categoríaPremio :: [Int] -> [Int] -> Int
categoríaPremio apuesta ganadora =
calcularCategoría
(númeroAciertos ((init . init) apuesta) (take 5 ganadora))
(númeroAciertos (drop ((length apuesta)-2) apuesta)
(drop 5 ganadora))
Código del Euromillón
• Obtención del número de aciertos.
númeroAciertos :: [Int] -> [Int] -> Int
númeroAciertos [] _ = 0
númeroAciertos (x:xs) ys
| elem x ys = 1 + númeroAciertos xs ys
| otherwise = númeroAciertos xs ys
• Cálculo de la categoría en función del número de aciertos.
calcularCategoría :: Int -> Int -> Int
calcularCategoría números estrellas = case (números,estrellas) of
(5,_) -> 3 - estrellas
(4,_) -> 6 - estrellas
(3,0) -> 10
(3,_) -> 9 - estrellas
(2,2) -> 9
(2,1) -> 12
(1,2) -> 11
(_,_) -> 0
Código del Euromillón
• Obtención del premio obtenido según la categoría.
calcularPremioCategoría :: Int -> [Float] -> Float
calcularPremioCategoría categoría (ventas:bote:premios) = case categoría of
0 -> 0.0
1 -> bote/(premios!!0 + 1)
2 -> (ventas/2)*0.074/(premios!!2 + 1)
3 -> (ventas/2)*0.021/(premios!!4 + 1)
4 -> (ventas/2)*0.015/(premios!!6 + 1)
5 -> (ventas/2)*0.010/(premios!!8 + 1)
6 -> (ventas/2)*0.007/(premios!!10 + 1)
7 -> (ventas/2)*0.010/(premios!!12 + 1)
8 -> (ventas/2)*0.051/(premios!!14 + 1)
9 -> (ventas/2)*0.044/(premios!!16 + 1)
10 -> (ventas/2)*0.047/(premios!!18 + 1)
11 -> (ventas/2)*0.101/(premios!!20 + 1)
12 -> (ventas/2)*0.240/(premios!!22 + 1)
Código de la Primitiva
• En la primitiva hay que tener en cuenta el reintegro.
calcularPremioDia :: [Int] -> [Int] -> [Float] -> Float
calcularPremioDia apuesta ganadora premios =
calcularPremioCategoria (categoriaPremio
apuesta
ganadora)
premios + reintegro
where
reintegro = if last apuesta == last ganadora
then
calcularPrecioApuesta apuesta
else 0
Código de la Primitiva
• Hay seis números en la combinación, no hay estrellas y hay
que tener en cuenta el número complementario.
categoriaPremio :: [Int] -> [Int] -> Int
categoriaPremio apuesta ganadora
=
calcularCategoria (numeroAciertos (init apuesta)(take
6
ganadora)) (numeroAciertos (init apuesta) comple)
where
comple = take 1 (drop 6 ganadora)
Código de la Primitiva
• Hay cinco categorías en vez de doce, la categoría depende
de la combinación ganadora y del complementario.
calcularCategoria :: Int -> Int -> Int
calcularCategoria apuesta complementario = case
(apuesta,complementario)of
(6,_) -> 1
(5,1) -> 2
(5,_) -> 3
(4,_) -> 4
(3,_) -> 5
(_,_) -> 0
Código de la Quiniela
• En nuestro programa de la Quiniela, la entrada es un
String.
o Por ejemplo: "1 1x 2 1 1 x 1x2 2 1 1 1 x 2 2 1x".
• Para manejar la apuesta mejor, hacemos una traducción a
lista de enteros.
traducir :: [String] -> [Int]
traducir [] = []
traducir (x:xs)
| x=="1" = (1:traducir xs)
| x=="2" = (2:traducir xs)
| x=="x" = (3:traducir xs)
| x=="12" = (4:traducir xs)
| x=="1x" = (5:traducir xs)
| x=="x2" = (6:traducir xs)
| x=="1x2" = (7:traducir xs)
| x=="?" = (8:traducir xs) -- Esto es para los patrones.
Código de la Quiniela
• Para calcular el precio hay que saber cuántas apuestas se
han hecho, en función de los dobles y triples que contenga.
númeroApuestas :: [Int] -> Int
númeroApuestas [] = 1
númeroApuestas (x:xs)
| esDoble x = 2 * númeroApuestas xs
| esTriple x = 3 * númeroApuestas xs
| otherwise = númeroApuestas xs
• Los dobles son 4, 5 y 6 (12, 1x, x2).
• El triple es 7 (1x2).
Código de la Quiniela
• También es diferente con respecto al Euromillón y la
Primitiva el hecho de que el orden a la hora de hacer la
apuesta importa.
o No es lo mismo "1 2 x..." que "1 x 2...".
númeroAciertos :: [Int] -> [Int] -> Int
númeroAciertos apuesta ganadora = sum (zipWith (\x y -> if (acierta x y) then 1
else 0) apuesta ganadora)
• La función acierta tiene en cuenta los dobles y triples.
o Si apuesta = "x2 1..." y ganadora = "x x..." devolvería un
acierto en el primer elemento de las combinaciones.
Código de la Quiniela
• El número de aciertos nos sirve para conocer la categoría
del premio al que hubiéramos optado.
calcularCategoría :: Int -> Int -> Int
calcularCategoría aciertos aciertoPleno = case (aciertos,aciertoPleno) of
(14,1) -> 6 -- Pleno al 15.
(14,0) -> 1 -- Categoría 1.
(13,_) -> 2 -- Categoría 2.
(12,_) -> 3
(11,_) -> 4
(10,_) -> 5
(_,_) -> 0 -- Categoría 0 == No premiado.
Programas de búsqueda
de patrones
Búsqueda de Patrones
• ¿Qué es un patrón?
o
Definición de la RAE: Modelo que sirve de muestra para
sacar otra cosa igual.
o
Nuestra definición: Conjunto de apuestas que tienen
algo en común.
Búsqueda de Patrones
• Diferenciamos dos tipos de patrones:
Patron numérico: Conjunto de apuestas cuyos números
guardan algún tipo de relación matemática.
o Ejemplos:
 Apuesta con números primos.
 Apuesta con números menores que diez.
 Apuesta de números pares.
o
o
Patrón visual: Conjunto de apuestas que al ser marcadas
sobre el boleto, representan una figura.
Búsqueda de Patrones
• Ejemplo: patrones visuales en el boleto de la Quiniela.
Búsqueda de Patrones
• Nuestro objetivo es conocer si la gente juega de forma
aleatoria o sigue algún patrón.
• ¿Cómo podemos saber a qué juega la gente si el número de
apuestas no es público?
• Sólo tenemos información sobre las ventas de cada sorteo y
el número de acertantes de cada categoría.
Ratio ventas/acertantes:
o La ratio de las ventas entre la suma de los acertantes de
N categorías, es un indicador de la cantidad de gente
que ha ganado apostando con un determinado patrón.
Búsqueda de Patrones
• Necesitamos poder comparar la ratio de cada combinación
con alguna referencia.
• Nuestra referencia será la media de esa ratio en todos los
sorteos de los que tenemos información.
imprimirMedia :: [String] -> Float
imprimirMedia [] = 0.0
imprimirMedia (combinacion:premios:historia) =
(head . calcularRatio . read) premios +
imprimirMedia historia
Búsqueda de Patrones
• Esto nos permite conocer cuánto ha sido apostada
una combinación o un conjunto de combinaciones
(patrón).
• Cuanto mayor sea nuestro indicador para una
combinación, menos acertantes se habrán producido y
viceversa.
• Los programas que hemos implementado utilizan este
indicador de forma que, dado un patrón y un predicado,
nos devuelven el conjunto de combinaciones que lo
cumplen, las ventas, los acertantes y la ratio ventas entre
acertantes, para cada combinación seleccionada.
• El predicado servirá para filtrar las combinaciones en
función del número de coincidencias.
Patrones numéricos: Euromillón
• Lectura de ficheros y llamada al programa de cómputo.
•
•
•
•
patrón :: (Int -> Bool) -> (Float -> Bool) -> Int -> IO ()
patrón f g n = do
x <- readFile historiaEuromillón
imprimirPatron (lines x) f g n
• Selección de combinaciones a imprimir.
imprimirPatron :: [String] -> (Int -> Bool) -> (Float -> Bool)
-> Int -> IO ()
imprimirPatron (combinacion:premios:historia) f g n
| (length (filter f (take 5 (read combinacion))) >= n) && ((g . head . calcularRatio . read)
premios) = do
imprimirPatron historia f g n
print (combinacion ++ " --> " ++ show((calcularRatio . read) premios)))
| otherwise = imprimirPatron historia f g n
imprimirPatron [] _ _ _ = putStr ""
Patrones numéricos: Euromillón
• Calculamos la ratio de la siguiente forma: Las ventas que se
produjeron para ese sorteo entre el número de acertantes
totales.
calcularRatio :: [Float] -> [Float]
calcularRatio (ventas:bote:acertantes) = [ventas/(sumarAcertantes acertantes
8),ventas,sumarAcertantes acertantes 8]
• Para que el valor obtenido sea más representativo no
hemos tenido en cuenta los acertantes de las últimas
categorías ya que siempre hay muchos y no nos dan una
información significativa.
Patrones visuales: Primitiva
• Todas las apuestas de un patrón cumplen que:
o
La distancia de todos los números respecto al primero
siempre es la misma.
o
La distancia en columnas de todos los números respecto
al primero siempre es la misma.
Patrones visuales: Primitiva
• Ejemplo: Las dos apuestas pertenecen al mismo
patrón.
o [1,11,12,21,22,32] == [17,27,28,37,38,48]
Patrones visuales: Primitiva
• La función patron recibe como parámetro la primera
combinación que pertenece a un patrón y un predicado p
que sirve para filtrar las combinaciones en función del
número de coincidencias con el patrón.
• Muestra la combinación, la ratio, las ventas, el nº de
acertantes y dibuja la combinación.
patron :: [Int]-> (Int -> Bool) -> IO[()]
patron pat p = do
x <- readFile historiaPrimitiva
dibujaBoleto pat
mapM (sequence_ .
dibujaResultados)(buscaPatrones
(sort pat) (lines x) p)
Patrones visuales: Primitiva
• Selección de combinaciones que pertenecen al patrón.
buscaPatrones :: [Int] -> [String] -> (Int -> Bool) -> [([Int],[Float])]
buscaPatrones _ [] _ = []
buscaPatrones [] _ _ = []
buscaPatrones patron (ganadora:premios:restoHistoria) p
| encajaPatron ((take 7 . read) ganadora)
(creaTodosPatrones patron) p =
(((sort . take 7 . read)ganadora),calcularRatio (read
premios)):buscaPatrones patron restoHistoria p
|otherwise =
buscaPatrones patron restoHistoria p
Patrones visuales: Primitiva
• La función creaTodosPatrones recibe la menor apuesta
que es cubierta por el patrón, y devuelve lista con todas
las apuestas cubiertas por el patrón.
creaTodosPatrones :: [Int] -> [[Int]]
creaTodosPatrones patron = creaPatrones (sort
patron)
(creaDesp (sort patron))
• encajaPatron comprueba que una combinación pertenece a
un patrón, satisfaciendo el predicado p.
encajaPatron :: [Int] -> [[Int]] -> (Int -> Bool) -> Bool
encajaPatron apuesta patron p = p (foldr (\x y -> max
apuesta) y) 0 patron)
(numeroAciertos x
Patrones visuales: Primitiva
• H
emos definido una función constante que devuelve la
estructura del boleto.
_boleto =
" 00 10 20 30 40\n 01 11 21 31 41\n 02 12 22 32 42\n 03 13 23 33 43" ++
"\n 04 14 24 34 44\n 05 15 25 35 45\n 06 16 26 36 46\n 07 17 27 37 47" ++
"\n 08 18 28 38 48\n 09 19 29 39 49"
• Sobre _boleto, sustituimos los números de una combinación
por la cadena "__", y los mostramos por pantalla.
Patrones visuales: Primitiva
• 
Funciones para crear un boleto y mostrarlo por pantalla.
dibujaBoleto :: [Int] -> IO()
dibujaBoleto = do putStrLn . ("\n" ++) . creaBoletoVisual
creaBoletoVisual :: [Int] -> String
creaBoletoVisual combinacion =
unlines (map unwords (sustituyeBoleto combinacion
"__" (map words (lines _boleto))))
Patrones visuales: Quiniela
• Hemos implementado la función pintar, que dado un patrón
o una apuesta, la muestra por pantalla como si fuera el
boleto.
pintar :: String -> IO()
pintar apuesta =
(putStrLn . ("\n 1 X 2\n " ++) . unwords) (zipWith (++) pintaColumna (map traducirGráfico (words apuesta)))
pintaColumna :: [String]
pintaColumna = map (\x -> if (x<10) then " "++show(x) else show(x)) [1..15]
traducirGráfico :: String -> String
traducirGráfico simb = case simb of
"?" -> "
\n"
"1" -> " *
\n"
"x" -> " * \n"
"2" -> "
* \n"
"1x" -> " * * \n"
"12" -> " * * \n"
"x2" -> " * * \n"
"1x2" -> " * * * \n"
Patrones visuales: Quiniela
• La función patrón, a la que se le pasa un patrón (puede
tener filas no marcadas, señaladas con '?') y un número
mínimo de aciertos que debe proporcionarnos dicho
patrón, nos lo muestra por pantalla y nos da una relación
de combinaciones ganadoras que cumplen ese mínimo y los
aciertos y la ratio anteriormente explicado.
patrón :: String -> Int -> IO()
patrón p error = do
x <- readFile historiaQuiniela
pintar p
imprimirPatrón (lines x) (traducir (words p)) error
Patrones visuales: Quiniela
imprimirPatrón :: [String] -> [Int] -> Int -> IO()
imprimirPatrón (combinación:premios:historia) patrón error
| (númeroAciertos patrón (read combinación)) >= error = do
imprimirPatrón historia patrón error
print ((bonita (read combinación)) ++ " --> " ++ show (númeroAciertos patrón (read
combinación)) ++ " --> " ++ show (calcularRatio (read premios)))
| otherwise = imprimirPatrón historia patrón error
imprimirPatrón [] _ _ = putStr ""
Resultados curiosos
Resultados curiosos: Euromillón
• La media del Euromillón es: 995.69
• Nunca han salido todos los números primos.
*Main> patrón esPrimo (\x->True) 5
• Combinaciones en las que todos los números han sido
primos menos 1.
*Main> patrón esPrimo (\x->True) 4
"[1,3,13,16,43,2,7] --> [781.4534,4.447564e7,56914.0]"
"[11,24,29,31,41,1,3] --> [940.5416,6.571188e7,69866.0]"
"[1,7,17,31,36,2,6] --> [1068.585,6.2435284e7,58428.0]"
"[1,11,18,19,47,3,7] --> [894.38617,8.023628e7,89711.0]"
"[5,13,17,40,43,1,3] --> [911.02264,8.32784e7,91412.0]"
"[9,11,13,19,37,3,4] --> [700.02014,7.2343584e7,103345.0]"
"[11,13,18,37,47,1,6] --> [1120.8005,8.1279336e7,72519.0]"
"[30,47,2,37,5,6,3] --> [1246.2428,8.371511e7,67174.0]"
"[38,47,31,19,5,3,5] --> [994.6379,1.358188e8,136551.0]"
Resultados curiosos: Euromillón
• Combinaciones acertadas por pocos jugadores.
*Main> patrón (\x->True) (>media+380) 5
"[26,36,37,38,42,2,6] --> [1388.5532,6.4070624e7,46142.0]"
"[35,36,42,46,49,2,8] --> [1376.8063,9.309964e7,67620.0]"
"[41,43,44,46,20,2,9] --> [1439.7476,9.216832e7,64017.0]"
• Combinaciones acertadas por muchos jugadores.
*Main> patrón (\x->True) (<media-350) 5
"[10,12,14,24,26,5,8] --> [643.3405,4.1352e7,64277.0]"
"[5,10,17,22,33,5,7] --> [617.76953,7.101755e7,114958.0]"
• Números pares.
*Main> patrón even (<700) 5
"[10,12,14,24,26,5,8] --> [643.3405,4.1352e7,64277.0]"
"[10,20,30,36,40,3,5] --> [694.91895,7.7093616e7,110939.0]"
Resultados curiosos: Primitiva
• La media de la ratio para la Primitiva es 41.15.
• El patrón escalera es bastante jugado.
00 10 __ __ 40
01 __ __ 31 41
__ __ 22 32 42
03 13 23 33 43
04 14 24 34 44
05 15 25 35 45
06 16 26 36 46
....
[18,21,22,30,31,40,41] --> [39.88779,1.4704155e7,368638.0]
[9,13,18,19,27,28,33] --> [28.898842,1.5286129e7,528953.0]
[7,8,17,25,26,32,35] --> [28.170479,9128165.0,324033.0]
Resultados curiosos: Primitiva
• El patrón tejado también es bastante jugado.
00 __ 20 __ 40
__ 11 21 31 __
02 __ 22 __ 42
03 13 23 33 43
04 14 24 34 44
05 15 25 35 45
06 16 26 36 46
....
[4,8,9,30,37,39,48] --> [34.107815,1.842794e7,540285.0]
[2,9,17,35,37,38,46] --> [8.308153,5493658.0,661237.0]
[1,7,16,20,36,38,48] --> [10.617591,8330944.0,784636.0]
[6,15,35,38,44,46,48] --> [12.549679,7585377.0,604428.0]
Resultados curiosos: Quiniela
• L
a Media es 557.03.
• El patrón 'zigzag':
*Main> patrón "1 x 2 x 1 x 2 x 1 x 2 x 1 x 2" 11
1 X 2
1 *
2 *
3
*
4 *
5 *
6 *
7
*
8 *
9 *
10 *
11
*
12 *
13 *
14 *
15
*
"1 X 2 X 1 X 2 1 1 X 1 2 1 X X --> 11 --> 39.233353"
• Otro patrón, cubre más
con 5 triples:
*Main> patrón "1 1x2 1 1 1x2 1 1 1x2 1 1 1x2 1 1 1x2 1" 14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
X 2
* *
* *
* *
* *
* *
"1 1 X 1 1 1 1 2 1 1 X 1 1 1 1 --> 14 --> 46.875664"
"1 1 1 1 1 X 1 1 1 1 1 1 1 2 1 --> 14 --> 13.047872"
Resultados curiosos: Quiniela
• El patrón 'todos unos':
*Main> patrón "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1" 12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 X 2
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
• Los resultados muestran
que mucha gente juega a
este patrón:
"1 1 1 1 1 1 X 1 2 1 1 1 2 1 1 --> 12 --> 64.979614"
"1 1 X 1 1 1 1 2 1 1 X 1 1 1 1 --> 12 --> 46.875664"
"1 1 1 X 1 1 1 X 1 1 1 X 1 1 1 --> 12 --> 38.72563"
"1 1 X 1 1 1 1 1 1 1 X X 1 1 1 --> 12 --> 14.160672"
"2 1 1 1 X 1 1 1 1 1 1 X 1 1 1 --> 12 --> 15.485243"
"1 1 1 1 1 1 1 1 2 1 1 1 X 1 2 --> 12 --> 12.125318"
"1 1 1 1 1 X 1 1 1 1 1 1 1 2 1 --> 13 --> 13.047872"
"X 1 1 X 1 1 1 1 1 X 1 1 1 1 1 --> 12 --> 13.555782"
Resultados curiosos: Quiniela
• Un patrón con prioridad al
empate:
*Main> patrón "x 1x x2 x x2 1x x 1x x2 x x2 1x x 1x x2" 12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 X
*
* *
*
*
*
* *
*
* *
*
*
*
* *
*
* *
*
2
*
*
*
*
*
• Los resultados muestran
que poca gente juega a
este patrón:
"X 1 X 2 X 1 X 1 X X 1 2 X X X --> 12 --> 5953.646"
"X 1 X X 2 1 X 1 X 1 X 1 1 1 2 --> 13 --> 703.6132"
"X X 1 X X 2 X X X X 1 X X 1 X --> 12 --> 1473.1707"
"1 1 1 X X 1 X X 1 X 2 X X X 2 --> 12 --> 1207.189"
"X 1 X X 2 X 2 2 1 X X 1 X 1 X --> 12 --> 4419.3354"
"X 1 X X 1 1 2 1 2 X X 1 2 X 2 --> 12 --> 18.934837"
"2 1 2 X X X X X X 1 X X X 1 1 --> 12 --> 173.81834"
"X X 1 X 1 X X 1 2 X 1 X X 1 X --> 12 --> 73.66204"
"1 1 2 2 1 1 X 1 2 X 2 1 X 1 X --> 12 --> 229.9559"
"1 1 2 X X X X X 1 2 X 1 X X 2 --> 12 --> 810.53253"
"X X X X X 1 2 1 2 2 X X X X X --> 13 --> 537.0503"
"1 1 2 X X 1 X 1 1 1 2 X X X 2 --> 12 --> 71.77404"
"1 1 X 1 X 1 X X X X X X X 1 1 --> 12 --> 342.74445"
"X 1 X 1 2 1 1 1 X 2 2 1 X 1 X --> 12 --> 10.901946"
"1 1 2 X 2 2 X X X X 2 1 2 1 2 --> 12 --> 772.0594"
"X X 2 X X 2 2 X X X 1 1 X 1 X --> 12 --> 1236.6622"
"X 1 1 2 2 X X 1 2 X X 1 X X 1 --> 12 --> 156.02274"
"1 X 2 X X 1 X X 2 X X 2 1 1 X --> 12 --> 538.5936"
"X 1 2 X 1 1 X 1 2 X X 1 2 1 X --> 13 --> 615.06494"
Ventajas de utilizar un
lenguaje declarativo
Ventajas de un lenguaje declarativo
• Lectura de fichero con la función readFile.
o Mientras que con un lenguaje imperativo habríamos
tenido que abrir el fichero, leerlo a trozos y finalmente
cerrarlo, Haskell nos ofrece esta función mediante la
que, con una sola llamada tenemos el fichero entero
disponible en una variable.
o En un lenguaje imperativo habríamos tenido que
construir una compleja estructura de datos para ir
almacenando los datos que leyéramos. Con Haskell una
variable es suficiente.
Ventajas de un lenguaje declarativo
• Funciones words y lines.
o Tras la lectura del fichero, el uso de estas funciones hace
que podamos tokenizar fácilmente el String de entrada.
o Es cierto que en lenguajes como Java hay clases que
permiten hacer este tipo de cosas, pero sería muy
ineficiente tener una variable con todo el fichero en
tokens.
Ventajas de un lenguaje declarativo
• Función read.
o Esta función nos ofrece una manera elegante y simple de
parsear el String de entrada y convertirlo al tipo inferido.
o En un lenguaje imperativo esta operación habría
necesitado conversiones de String al tipo deseado
utilizando funciones diferentes para cada tipo.
o Con Haskell sólo hemos usado una función para todos
los tipos, y además no hemos tenido que indicar el tipo
de la conversión explícitamente.
Ventajas de un lenguaje declarativo
• Implementación de apuestas múltiples
o Añadir a nuestros programas la posibilidad de realizar
apuestas múltiples requirió muy pocos cambios.
o Haskell ofrece funciones muy sencillas para realizar
operaciones como quitar los n últimos elementos de una
lista u obtener los m primeros de otra.
o En un lenguaje imperativo esto habría necesitado del uso
de índices que siempre son engorrosos.
Ventajas de un lenguaje declarativo
• Listas y operaciones sobre listas.
o Al ser todos los datos de entrada listas de números, el
completo manejo que ofrece Haskell a la hora de
manejarlas ha resultado muy útil.
 Pattern matching.
 Operaciones sobre listas.
 Maps y plegados.
• Funciones lambda.
o Nos han permitido pasar funciones como parámetro
fácilmente.
o Posibilidad de poder aplicarlas sin necesidad de
declararlas en el código hace que fácilmente podamos
probar patrones numéricos.
Gracias por su atención
Alejandro Pérez Roca
Daniel Martín Prieto
Jorge Tudela Glez de Riancho
Descargar

Loterías y Juegos de Azar