Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Come risolvere un puzzle crypto-aritmetico in Scala

Come risolvere un puzzle crypto-aritmetico in Scala

Bartosz Milewski ha scritto 4 blog post su come risolvere una semplice puzzle matematico come un vero Programmatore Funzionale.

Modelleremo il problema come un problema di soddisfacimento di vincoli per poi risolverlo in Scala combinando 2 diversi monad. L'implementazione Scala in stile FP sara' estremamente simile a quella in Haskell presentata da Bartosz.

Per quanto elegante, tuttavia questo non sara' l'unico approccio interessante che verra' presentato.

Filippo Vitale

December 20, 2016
Tweet

More Decks by Filippo Vitale

Other Decks in Programming

Transcript

  1. ~ scala Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit

    Server VM, Java 1.8.0_91). Type in expressions to have them evaluated. Type :help for more information. scala>
  2. ~ scala Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit

    Server VM, Java 1.8.0_91). Type in expressions to have them evaluated. Type :help for more information. scala> import scalaz._, Scalaz._ // o cats._
  3. Brute force 10! / (10 – 8)! ≈ 2 Milioni

    Ricerca esaustiva e spazio di ricerca
  4. for (int s = 0; s < 10; ++s) for

    (int e = 0; e < 10; ++e) for (int n = 0; n < 10; ++n) for (int d = 0; d < 10; ++d) ... s != 0 e != s n != s && n != e d != s && d != e && d != n Approccio Imperativo: 8 loop annidiati
  5. for (int s = 0; s < 10; ++s) for

    (int e = 0; e < 10; ++e) for (int n = 0; n < 10; ++n) for (int d = 0; d < 10; ++d) ... s != 0 e != s n != s && n != e d != s && d != e && d != n Ricerca soluzioni teoricamente possibili: ➔ Generazione delle assegnazioni ➔ Verifica dei vincoli
  6. for { s <- 0 to 9 if s !=

    0 e <- 0 to 9 if e != s n <- 0 to 9 if n != s && n != e d <- 0 to 9 if d != s && d != e && d!=n ... for (int s = 0; s < 10; ++s) for (int e = 0; e < 10; ++e) for (int n = 0; n < 10; ++n) for (int d = 0; d < 10; ++d) ... s != 0 e != s n != s && n != e d != s && d != e && d != n Ricerca soluzioni teoricamente possibili: ➔ Generazione delle assegnazioni ➔ Verifica dei vincoli
  7. val listOfDigits = (0 to 9).toList val solutions = for

    { s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y } yield ??? Ricerca soluzioni teoricamente possibili Verifica vincoli
  8. val listOfDigits = (0 to 9).toList val solutions = for

    { s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (send, more, money) Ricerca soluzioni teoricamente possibili Verifica vincoli
  9. val listOfDigits = (0 to 9).toList val solutions = for

    { s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (send, more, money)
  10. 0 1 2 3 4 5 6 7 8 9

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2
  11. 0 1 2 3 4 5 6 7 8 9

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2 S = 1
  12. 0 1 2 3 4 5 6 7 8 9

    S = 1 E = …
  13. 0 1 2 3 4 5 6 7 8 9

    S = 1 E = … 0 2 3 4 5 6 7 8 9
  14. val l = for { a <- List(1, 2) b

    <- List(1, 2) } yield Map('a' -> a, 'b' -> b) val l = ???
  15. val l = for { a <- List(1, 2) b

    <- List(1, 2) } yield Map('a' -> a, 'b' -> b) val l: List[A] = ???
  16. val l = for { a <- List(1, 2) b

    <- List(1, 2) } yield Map('a' -> a, 'b' -> b) val l: List[Map[Char, Int]] = ???
  17. val l = for { a <- List(1, 2) b

    <- List(1, 2) } yield Map('a' -> a, 'b' -> b) List(Map('a' -> 1, 'b' -> 1), Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1), Map('a' -> 2, 'b' -> 2))
  18. val l = for { a <- List(1, 2) b

    <- List(1, 2) if a != b } yield Map('a' -> a, 'b' -> b) List(Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1))
  19. “The future is a function of the past.” – A.

    P. Robertson https://goo.gl/jt6bvz
  20. // stateful computation type State[S, +A] = S => (S,

    A) https://bartoszmilewski.com/2016/11/30/monads-and-effects/#State
  21. trait State[S, +A] { def eval(initial: S): A def map[B](f:

    A => B): State[S, B] def flatMap[B](f: A => State[S, B]): State[S, B] } object State { def apply[S, A](f: S => (S, A)): State[S, A] }
  22. // stateful computation f: S => (S, A) def select(xs:

    List[Int]): (List[Int], Int) = (xs.tail, xs.head)
  23. // stateful computation f: S => (S, A) def select(xs:

    List[Int]): (List[Int], Int) = (xs.tail, xs.head)
  24. // stateful computation f: S => (S, A) def select(xs:

    List[Int]): (List[Int], Int) = (xs.tail, xs.head)
  25. import scalaz.State val s = for { a <- State(select)

    b <- State(select) } yield Map('a' -> a, 'b' -> b) val s = ???
  26. import scalaz.State val s = for { a <- State(select)

    b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ S, A ] = ???
  27. import scalaz.State val s = for { a <- State(select)

    b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ???
  28. import scalaz.State val s = for { a <- State(select)

    b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ??? s.eval(List(1, 2)) ==== Map('a' -> 1, 'b' -> 2)
  29. import scalaz.State val s = for { a <- State(select)

    b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ??? s.eval(List(8, 3)) ==== Map('a' -> 8, 'b' -> 3)
  30. val s = for { a <- State(select) b <-

    State(select) c <- State(select) } yield Map('a' -> a, 'b' -> b, 'c' -> c) s.eval(1 |-> 3) ==== Map('a' -> 1, 'b' -> 2, 'c' -> 3)
  31. val s = for { a <- State(select) b <-

    State(select) c <- State(select) d <- State(select) } yield Map('a' -> a, 'b' -> b, 'c' -> c, 'd' -> d) s.eval(1 |-> 4) ==== Map('a' -> 1, 'b' -> 2, 'c' -> 3, 'd' -> 4)
  32. State S => ( S , A) List[( S ,

    A)] StateT[F, S, A] S => F[( S , A)]
  33. def select[A](xs: List[A]): (List[A], A) State S => ( S

    , A) def select[A](xs: List[A]): List[(List[A], A)] StateT[F, S, A] S => F[( S , A)]
  34. 0 1 2 3 4 5 6 7 8 9

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2
  35. sl.eval(1 |-> 2) == ??? import scalaz.StateT import scalaz.std.list._ val

    sl = for { a <- StateT(select) b <- StateT(select) } yield Map('a' -> a, 'b' -> b)
  36. import scalaz.StateT import scalaz.std.list._ val sl = for { a

    <- StateT(select) b <- StateT(select) } yield Map('a' -> a, 'b' -> b) sl.eval(1 |-> 2) == List(Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1)) https://gist.github.com/filippovitale/6cc45396ed917a2a8411
  37. val sel = StateT(select[Int]) for { s <- sel e

    <- sel n <- sel d <- sel m <- sel o <- sel r <- sel y <- sel … }
  38. val sel = StateT(select[Int]) for { s <- sel e

    <- sel n <- sel d <- sel m <- sel o <- sel r <- sel y <- sel … } Lo stesso `sel` restituisce ad ogni `.flatMap` un numero diverso
  39. val sel = StateT(select[Int]) for { s <- sel e

    <- sel n <- sel d <- sel m <- sel o <- sel r <- sel y <- sel … } Lo stesso `sel` restituisce ad ogni `.flatMap` un numero diverso 0 1 2 3 4 5 6 7 8 9
  40. val sel = StateT(select[Int]) for { s <- sel if

    s != 0 e <- sel n <- sel d <- sel m <- sel if m != 0 o <- sel r <- sel y <- sel send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (s, e, n, d, m, o, r, y)
  41. val sel = StateT(select[Int]) for { s <- sel if

    s != 0 e <- sel n <- sel d <- sel m <- sel if m != 0 o <- sel r <- sel y <- sel send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (s, e, n, d, m, o, r, y) MonadPlus MonadPlus https://speakerdeck.com/filippovitale/send-plus-more-equals-money-scalasyd-july-2015
  42. val listOfDigits = (0 to 9).toList val solutions = for

    { s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (send, more, money) Ricerca soluzioni teoricamente possibili Verifica vincoli
  43. val maps = for { ??? } yield ??? Ricerca

    soluzioni teoricamente possibili Verifica vincoli
  44. "sendmoremoney".distinct.size // 8 distinct `char` val maps = for {

    ??? } yield ??? Ricerca soluzioni teoricamente possibili Verifica vincoli
  45. "sendmoremoney".distinct.size // 8 distinct `char` val maps = for {

    ??? } yield ("sendmoremoney".distinct zip ???).toMap Ricerca soluzioni teoricamente possibili Verifica vincoli
  46. val maps = for { ??? } yield ("sendmoremoney".distinct zip

    ???).toMap Ricerca soluzioni teoricamente possibili Verifica vincoli
  47. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni // Vector(0, 1, 2, 3, 4, 5, 6, 7) // Vector(0, 1, 2, 3, 4, 5, 6, 8) // Vector(0, 1, 2, 3, 4, 5, 6, 9) // Vector(0, 1, 2, 3, 4, 5, 7, 8) // Vector(0, 1, 2, 3, 4, 5, 7, 9) // Vector(0, 1, 2, 3, 4, 5, 8, 9) // Vector(0, 1, 2, 3, 4, 6, 7, 8) // Vector(0, 1, 2, 3, 4, 6, 7, 9) // Vector(0, 1, 2, 3, 4, 6, 8, 9) // ... } yield ("sendmoremoney".distinct zip ???).toMap Ricerca soluzioni teoricamente possibili Verifica vincoli
  48. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni // Vector(0, 1, 2, 3, 4, 5, 6, 7) // (8, 9) // Vector(0, 1, 2, 3, 4, 5, 6, 8) // (7, 9) // Vector(0, 1, 2, 3, 4, 5, 6, 9) // (7, 8) // Vector(0, 1, 2, 3, 4, 5, 7, 8) // (6, 9) // Vector(0, 1, 2, 3, 4, 5, 7, 9) // (6, 8) // Vector(0, 1, 2, 3, 4, 5, 8, 9) // (6, 7) // Vector(0, 1, 2, 3, 4, 6, 7, 8) // (5, 9) // Vector(0, 1, 2, 3, 4, 6, 7, 9) // (5, 8) // Vector(0, 1, 2, 3, 4, 6, 8, 9) // (5, 7) // ... } yield ("sendmoremoney".distinct zip ???).toMap Ricerca soluzioni teoricamente possibili Verifica vincoli
  49. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni permutation <- combination.permutations // 40K permutazioni } yield ("sendmoremoney".distinct zip permutation).toMap Ricerca soluzioni teoricamente possibili Verifica vincoli
  50. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni permutation <- combination.permutations // 40K permutazioni } yield ("sendmoremoney".distinct zip permutation).toMap // ~2M Ricerca soluzioni teoricamente possibili Verifica vincoli
  51. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni permutation <- combination.permutations // 40K permutazioni } yield ("sendmoremoney".distinct zip permutation).toMap // ~2M val solutions = for { m <- maps if m('s') != 0 && m('m') != 0 send = "send" map m reduce (_ * 10 + _) more = "more" map m reduce (_ * 10 + _) money = "money" map m reduce (_ * 10 + _) if send + more == money } yield (send, more, money) Ricerca soluzioni teoricamente possibili Verifica vincoli
  52. val maps = for { combination <- (0 to 9).combinations(8)

    // 45 combinazioni permutation <- combination.permutations // 40K permutazioni } yield ("sendmoremoney".distinct zip permutation).toMap // ~2M val solutions = for { m <- maps if m('s') != 0 && m('m') != 0 send = "send" map m reduce (_ * 10 + _) more = "more" map m reduce (_ * 10 + _) money = "money" map m reduce (_ * 10 + _) if send + more == money } yield (send, more, money) solutions.next() // (9567,1085,10652) Ricerca soluzioni teoricamente possibili Verifica vincoli
  53. Risolvere il puzzle a compile-time Peano's arithmetic Shapeless: HList, Sum,

    … http://jto.github.io/articles/typelevel_quicksort/
  54. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit]
  55. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] // “sendmory”
  56. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] // “sendmory” Vector di lunghezza 8 2 Digit non utilizzate
  57. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] // “sendmory” List(0 |-> 9) .combinations(2) .toSet .size // 45 Vector di lunghezza 8 2 Digit non utilizzate ⇒ 45 Specie diverse
  58. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] type Population = List[Individual] def fitness(individual: Individual): Int
  59. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] type Population = List[Individual] def fitness(individual: Individual): Int // 0
  60. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] type Population = List[Individual] def fitness(individual: Individual): Int def mutation(individual: Individual): Individual
  61. Algoritmo genetico - Individuo - Popolazione - Generazione - Funz.

    di Fitness - Mutazione type Digit = Int type Individual = Vector[Digit] type Population = List[Individual] def fitness(individual: Individual): Int def mutation(individual: Individual): Individual // “asexual genetic algorithm in this paper”
  62. /** side-effect */ val r = util.Random val generationSize =

    512 val generationRetainment = 32 // top 6% // 16 of the 28 possible mutations val individualProlificness: Int = generationSize / generationRetainment
  63. def fitness(individual: Individual): Int = { val cs = "sendmoremoney".distinct

    val m = cs.zip(individual).toMap math.abs(send + more - money) }
  64. def fitness(individual: Individual): Int = { val cs = "sendmoremoney".distinct

    val m = cs.zip(individual).toMap val send = "send" map m reduce (_ * 10 + _) val more = "more" map m reduce (_ * 10 + _) val money = "money" map m reduce (_ * 10 + _) math.abs(send + more - money) }
  65. val mutationIndices = List(0 |-> 7).combinations(2) def everyMutation(individual: Individual): Iterator[Individual]

    = r.shuffle(mutationIndices) .map { case Seq(i, j) => individual .updated(i, individual(j)) .updated(j, individual(i)) } // +-+-+-+-+-+-+-+-+ // |S|E|N|D|M|O|R|Y| // +-+-+-+-+-+-+-+-+ // |0|1|2|3|4|5|6|7| // +-+-+-+-+-+-+-+-+ // ^ ^ // | | // +-----+ // [i] [j]
  66. def createNextGeneration(currentGeneration: Population) : Population = if (fitness(currentGeneration.head) == 0)

    { ??? } else { currentGeneration.take(generationRetainment) .flatMap(everyMutation(_).take(individualProlificness)) .sortBy(fitness) }
  67. def createNextGeneration(currentGeneration: Population) : Population = if (fitness(currentGeneration.head) == 0)

    { currentGeneration.take(1) } else { currentGeneration.take(generationRetainment) .flatMap(everyMutation(_).take(individualProlificness)) .sortBy(fitness) }