ScalaでProject Euler(73)

Problem 43

まず、1000未満の17の倍数を生成します。例えば、289ですね。その上2桁を取って、28。その上の桁に数字をつけた数が13の倍数になっている必要があります。

13n = 100d + 28

このディオファントス方程式を0 ≤ d < 10の範囲で解いて、d = 7。これを数字のダブりをチェックしながら繰り返します。

def gcd(n :Int, m :Int) :Int =
    if(m == 0) n else gcd(m, n % m)

def digits(n :Int) :Iterator[Int] =
    if(n == 0) Iterator() else Iterator(n % 10) ++ digits(n / 10)

def number(ds :List[Int]) =
    ds.foldLeft(0L)((x, y) => x * 10 + y)

// ax + by = c
def solve_linear_equation(a :Int, b :Int, c :Int) :Option[(Int,Int)] =
    if(b == 0)
        if(c % a == 0) Some((c / a, 0)) else None
    else {
        val (a1, b1) = (b, a % b)
        solve_linear_equation(a1, b1, c) match {
            case None => None
            case Some((x, y)) =>
                if(b1 == 0)
                    Some((0, c / a1))
                else
                    Some((y, x - a / b * y))
        }
    }

def add_flag(flag :Option[Int], n :Int) :Option[Int] = flag match {
    case None => None
    case Some(f) => {
        if((f & (1 << n)) != 0)
            None
        else
            Some(f | (1 << n))
    }
}

def residual_flag(flag :Int) :Int =
    if((flag & 1) == 0) 0 else 1 + residual_flag(flag >> 1)

def gen_digits(p :Int, ds :List[Int]) :Iterator[Int] = ds match {
    case d1 :: d2 :: Nil => {
        val c = d1 * 10 + d2
        solve_linear_equation(p, 100, c) match {
            case None => Iterator()
            case Some((x0, y0)) => {
                val r = (-y0) % p
                val d0 = if(r >= 0) r else p + r
                val diff = p / gcd(p, 100)
                Iterator.range(d0, 10, diff)
            }
        }
    }
    case _ => Iterator()
}

def digits2(n :Int, rep :Int) :List[Int] =
    if(rep == 0) Nil else digits2(n / 10, rep - 1) ++ List(n % 10)

def gen_number(ps :List[Int], ds :List[Int], flag :Option[Int])
                                :Iterator[List[Int]] = (ps, ds, flag) match {
    case (_, _, None) => Iterator()
    case (Nil, _, Some(f)) => {
        val d = residual_flag(f)
        if(d > 0) Iterator(List(d)) else Iterator()
    }
    case (p :: pst, Nil, Some(f)) =>
        for(n <- Iterator.range(p, 1000, p);
            ds2 = digits2(n, 3);
            ds3 <- gen_number(pst, ds2.slice(0, 2),
                        ds2.foldLeft(flag)((x, y) => add_flag(x, y))))
                yield ds3 ++ ds2
    case (p :: pst, _, _) =>
        for(d <- gen_digits(p, ds);
            ds2 <- gen_number(pst, List(d, ds.head), add_flag(flag, d)))
                yield ds2 ++ List(d)
}

val ps = List(17, 13, 11, 7, 5, 3, 2)
println (gen_number(ps, Nil, Some(0)).map(number).sum)