1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Project Euler】Problem 78: コインの分割

Last updated at Posted at 2022-02-08
  • 本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

問題 78.コインの分割

原文 Problem 78: Coin partitions

問題の要約:コイン$n$個を分割する方法の数$p(n)$が$10^6$の倍数になる最小の$n$を求めよ

分割問題3部作の最後です。「Problem 76: 整数の分割」と同じなのですが数が全然大きいのでまた違ったアプローチが必要になります。とても100番以下の問題とは思えませんね。

とりあえずProblem 76で紹介したnpartitionで単純に探して見ます。5分以上かかりますが答えは見つかります。

from sympy.ntheory import npartitions
from itertools import count
for n in count():
  p = npartitions(n)
  if p % 10**6 == 0:
    break
print(f"Answer: {n}")

これを超える方法なんてあるのと思いますが探すとあるんですね。「分割数(Wikipedia)」の「分割数の母函数」セクションで以下のような漸化式が紹介されてます。

\begin{align}
&p(k) = p(k − 1) + p(k − 2) − p(k - 5) − p(k − 7) + p(k − 12) + p(k − 15) − p(k − 22) − ...  \\
&ここで1,2,5,7,12,15,22 は\frac{n(3n − 1)}{3} の形の一般五角数でnの符号が交互(+/-)\\
&また各項の符号は +, +, −, −, +, +, ... と続く
\end{align}

これをPythonで実装したのがこちらです。

from itertools import count
from functools import lru_cache
@lru_cache(maxsize=None)
def pentnum(m):
  k = [1, -1][m%2] * (m // 2 + 1)
  return (k * (3 * k - 1)) // 2

p = [1]
for n in count(1):
  p.append(0)
  for m in count(0):
    penta = pentnum(m)
    if penta > n: break
    p[n] += [1,1,-1,-1][m%4] * p[n - penta]
    p[n] %= 10**6
  if (p[n] == 0): break
print(f"Answer : {n}")

(開発環境:Google Colab)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?