0
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?

やさしい母関数(Generating Function)(その7)重複組合せ

0
Posted at

前回の(その6)では色々な分割数の問題を解く関数をnumpyのconvolveを使って実装しました。今回は重複組合せを解く関数を作ります。

両替問題を解く(個数制限あり)

(その5)で作ったコードのデータに 最低使う個数 を加えて1つのコインを (w, m0, m1) で表します

  • w: 重み=コインの価値
  • m0: 最低使う個数
  • m1: 最大個数(制限なしは0)

例えば 5pのコインは最低1個 最大10個使うならデータは(5,1,10) となります。

例題1 1p,2p,5p,10pのコインを使って20pにするのは何通りあるか求めよ。ただし1p,2p,5pは最低1個で最大5個までしか使えない

この時のデータは以下の様になります。これを関数 polに与えて多項式の係数を作ります。

def pol(pd, N):
  w, m0, m1 =  pd
  return [1 if (i % w == 0) and (i >= m0*w) else 0 for i in range((N if m1==0 else min(N, m1*w))+1)]
  
N, pdata = 20,  [(1,1,5),(2,1,5),(5,1,5),(10,0,0)]
for pd in pdata:
  print(pd, pol(pd, N))
# (1, 1, 5) [0, 1, 1, 1, 1, 1]
# (2, 1, 5) [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
# (5, 1, 5) [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
# (10, 0, 0) [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]  

重複組合せ(制限あり)の場合

典型的な重複組合せの問題を考えます。

例題2 赤の玉が1つ,青の玉が2つ,黄の玉が2つある。この中から4つ玉を選ぶときに得られる色のパターンが何通りあるか求めよ

この場合は色の違う1pコインと考えればよいので

N, pdata = 4,  [(1,0,1),(1,0,2),(1,0,2)]
# (1, 0, 1) [1, 1]
# (1, 0, 2) [1, 1, 1]
# (1, 0, 2) [1, 1, 1]

重複組合せ(制限なし)の場合

例題3 青,赤,黒の三種類の玉がたくさんある。この中から4つ玉を選ぶときに得られる色のパターンが何通りあるか求めよ。

N, pdata = 4,  [(1,0,0),(1,0,0),(1,0,0)]

となりますが、同じ値の繰り返しになるので、これを以下のように表現します。

N, pdata = 4,  [(1,0,0),('*', 3)]  # repeat (1,0,0) 3 times

データから解を求める関数 polmult

データからnp.convolveを使って解を求める関数 polmultです。リピート記号"*"がある場合にジェネレーターを変更してます。

import numpy as np
from functools import reduce

def polmult(pdata, N):
  if pdata[-1][0] == '*':
    gen = (pol(pdata[0], N) for _ in range(pdata[-1][1]))  # repeat pdata[-1][1] times
  else: 
    gen = (pol(pd, N) for pd in pdata)
  return reduce(lambda x, y: np.convolve(x,y)[:N+1], gen, [1])

例題1-3の解答

あとはデータをpolmultに与えればOKです。polmultのリターンは多項式の係数全部なのでN番目の要素を取り出せば答えになります。

title, N, pdata = "例題1", 20, [(1,1,5),(2,1,5),(5,1,5),(10,0,0)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# # 例題1の答え: 7
title, N, pdata = "例題2", 4,  [(1,0,1),(1,0,2),(1,0,2)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題2の答え: 3
title, N, pdata = "例題3", 4,  [(1,0,0),('*', 3)]  # repeat (1,0,0) 3 times
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題3の答え: 15

応用例

例題4 コインを10回投げたときに表が0-10回出る場合の数

すなわち二項係数です。多項式の係数をそのままプリント

title, N, pdata = "例題4", 10,  [(1,0,1),('*', 10)]  
print(f"# {title}の答え: {polmult(pdata, N)}")
# 例題4の答え: [  1  10  45 120 210 252 210 120  45  10   1]

例題5 サイコロを3回投げたときに目の和が10になる場合の数

サイコロは0がないので最小値=1、最大値=6とします

title, N, pdata = "例題5", 10,  [(1,1,6),('*', 3)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題5の答え: 27

例題6 x+y+z = 10を満たす、負でない整数 (x,y,z) の組は何通り?

これは例題3と同じですね。

title, N, pdata = "例題6", 10,  [(1,0,0),('*', 3)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題6の答え: 66

例題7 x+y+z <= 10を満たす、負でない整数 (x,y,z)の組は何通り?

これは polmultが1-10の係数を返すことを利用すればデータは例題6と同じでsumを取ればいいです

print(f"# {title}の答え: {sum(polmult(pdata, N))}")
# 例題7の答え: 286

例題8(x+y+z+p+q)**4を展開して同類項をまとめたとき異なる項は何通りありますか

5文字から4個を選ぶ重複組合せなので

N, K = 4, 5
title, pdata = "例題8",  [(1,0,0), ("*", K)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題8の答え: 70

例題9 【分割数】10を自然数の和で何通りの組み合わせで表せるか?

両替問題に置き換えて1p-10pを組み合わせて10pにすると考えます。分割数に関しては(その6)で他の例も取り上げています。

title, N, pdata = "例題9", 10,  [(i,0,0) for i in range(1,10+1)]
print(f"# {title}の答え: {polmult(pdata, N)[N]}")
# 例題9の答え: 42

この考え方はProject Euler, Problem 881: Divisor Graph Widthを解くのに役に立ちます。

(開発環境:Google Colab)

0
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
0
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?