さらに速い方法があります。
Pk(x) = 1 / (1 - xk)
なので、
(1 - x)(1 - x2)...(1 - x100)(1 - x200)
を計算して逆数を取るだけです。本当は証明が必要なのですが手抜きします。
N = 5000で前回の方法より2桁速かったです。O(N2)がO(N)ですからね。
import scala.testing.Benchmark type Poly = Array[Long] val coins = List(1, 2, 5, 10, 20, 50, 100, 200) val N = 5000 val M = coins.sum def unit() : Poly = Iterator.range(0, N + 1).map(k => if(k == 0) 1L else 0L).toArray def mul(f :Poly, n :Int) = { val a = f.clone for(k <- Iterator.range(0, M - n + 1)) a(k + n) -= f(k) a } def div(f :Poly, g :Poly) = { val h = new Poly(N + 1) val a = f.clone for(k <- 0 to N) { val c = a(k) / g(0) h(k) = c for(l <- 0 to M.min(N - k)) a(k + l) -= c * g(l) } h } def solve() = { val f = coins.foldLeft(unit)(mul) val g = div(unit, f) println (g(N)) } object Test extends Benchmark { def run() = solve } println (Test.runBenchmark(10))