defmodule CPSolver.Examples.Euler43 do
@moduledoc """
https://projecteuler.net/problem=43
"""
@doc """
int: n = 10;
array[1..n] of var 0..9: x;
array[int] of int: primes = [2,3,5,7,11,13,17];
solve satisfy;
constraint
all_different(x) /\
forall(i in 2..8) (
(100*x[i] + 10*x[i+1] + x[i+2]) mod primes[i-1] = 0
)
;
output [ show(x),"\n"];
"""
alias CPSolver.IntVariable, as: Variable
alias CPSolver.Constraint.AllDifferent.FWC, as: AllDifferent
alias CPSolver.Constraint.Modulo
alias CPSolver.Model
import CPSolver.Constraint.Factory
import CPSolver.Variable.View.Factory
require Logger
@minizinc_solutions Enum.sort([
[4, 1, 6, 0, 3, 5, 7, 2, 8, 9],
[1, 4, 6, 0, 3, 5, 7, 2, 8, 9],
[4, 1, 0, 6, 3, 5, 7, 2, 8, 9],
[1, 4, 0, 6, 3, 5, 7, 2, 8, 9],
[4, 1, 3, 0, 9, 5, 2, 8, 6, 7],
[1, 4, 3, 0, 9, 5, 2, 8, 6, 7]
])
def model() do
primes = [2, 3, 5, 7, 11, 13, 17]
domain = 0..9
x = Enum.map(1..10, fn i -> Variable.new(domain, name: "x#{i}") end)
all_different_constraint = AllDifferent.new(x)
constraints =
Enum.reduce(2..8, [all_different_constraint], fn i, constraints_acc ->
x_i = Enum.at(x, i - 1)
x_i_1 = Enum.at(x, i)
x_i_2 = Enum.at(x, i + 1)
prime = Enum.at(primes, i - 2)
{sum_var, sum_constraint} = sum([mul(x_i, 100), mul(x_i_1, 10), x_i_2])
mod_constraint = Modulo.new(0, sum_var, prime)
[sum_constraint, mod_constraint | constraints_acc]
end)
Model.new(x, constraints)
end
def check_solution(solution) do
solution
|> Enum.take(10)
|> Kernel.in(@minizinc_solutions)
end
def run(opts \\ [search: {:input_order, :indomain_random}, space_threads: 8]) do
{:ok, res} = CPSolver.solve(model(), opts)
Enum.sort(Enum.map(res.solutions, fn s -> Enum.take(s, 10) end))
|> tap(fn sorted_solutions ->
(sorted_solutions == @minizinc_solutions &&
Logger.notice("Solutions correspond to the ones given by MinZinc")) ||
Logger.error("Solutions do not match MiniZinc")
end)
|> Enum.reduce(0, fn s, sum_acc -> Integer.undigits(s) + sum_acc end)
end
end