🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

1回答

754閲覧

「プログラマ脳を鍛える数学パズル」の Q30 がわかりません

pyxis

総合スコア19

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

2グッド

1クリップ

投稿2020/01/06 03:22

「プログラマ脳を鍛える数学パズル」という本に載っている
「Q30 テーブルタップで作るタコ足配線」の解答コードが理解できません。
どなたかコードのアルゴリズムを解説していただけないでしょうか?

問題は以下です。

口数が2つと3つのテーブルタップを組み合わせてぴったりN個の空いている口を作る。
N=20の時、作れるパターン数を求めよ。
(ただし、同じテーブルタップに挿す場合、挿す場所によっての場合分けは考えない)

例としN=4の場合、下図のように4パターン作ることが出来る。
イメージ説明
解答のコードは以下です

Ruby

1N = 20 2 3@memo = {1 => 1} 4def set_tap(remain) 5 return @memo[remain] if @memo.has_key?(remain) 6 cnt = 0 7 # 2口 8 (1..(remain / 2)).each{|i| 9 if remain - i == i then 10 cnt += set_tap(i) * (set_tap(i) + 1) / 2 11 else 12 cnt += set_tap(remain - i) * set_tap(i) 13 end 14 } 15 # 3口 16 (1..(remain / 3)).each{|i| 17 (i..((remain - i) / 2)).each{|j| 18 if (remain - (i + j) == i) && (i == j) then 19 cnt += set_tap(i) * (set_tap(i) + 1) * (set_tap(i) + 2) / 6 20 elsif remain - (i + j) == i then 21 cnt += set_tap(i) * (set_tap(i) + 1) * set_tap(j) / 2 22 elsif i == j then 23 cnt += set_tap(remain - (i+j)) * set_tap(i) * (set_tap(i)+1) / 2 24 elsif remain - (i + j) == j then 25 cnt += set_tap(j) * (set_tap(j) + 1) * set_tap(i) / 2 26 else 27 cnt += set_tap(remain - (i + j)) * set_tap(j) * set_tap(i) 28 end 29 } 30 } 31 @memo[remain] = cnt 32end 33 34puts set_tap(N) 35

2口や3口の場合の初めに書いてある

(1..(remain / 2)) や (1..(remain / 3)) が

タップの必要となる最大の個数?かとも思ったのですが、その後の場合分けも含め、全く理解できません。

どなたか解説宜しくお願いします。

DrqYuto, ayunosioyaki👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コードを読む限り、最初に差し込み口が一つだけある場合にremain個の差し込み口をつくる場合の数を求めてるんだと思います。
'#2口とコメントが書かれてるブロックは、その最初の差し込み口に2つ差し込み口のあるタップをつけるときの場合の数で
'#3口とコメントが書かれているブロックは、その最初の差し込み口に3つ差し込み口のあるタップをつけるときの場合の数です。

新たに差し込み口が増えたので、それらの差し込み口をそれぞれを最初の一つとして再帰的に問題を解いていくことで全体の場合の数が計算できます。

試しにN==4について解くと、最初に2口のタップを挿した場合それぞれの差し込み口を使って(1, 3), (2, 2)個の差し込み口を作ると合計で4個の差し込み口が作れます(どの差し込み口に差すかで区別しないので、(3, 1)などは考えない)
図でいうところの上二つが(1, 3)のパターンで、左下が(2, 2)のパターンです。
最初に3口のタップを挿した場合は(1, 1, 2)のパターンしかありません。図でいうところの右下のパターン。

それを踏まえてコードにコメントをつけるとこんな感じです。

Ruby

1N = 20 2 3@memo = {1 => 1} 4def set_tap(remain) 5 return @memo[remain] if @memo.has_key?(remain) 6 cnt = 0 7 # 2口 8 (1..(remain / 2)).each{|i| #2口の差し込み口のうち少ない数を割り当てる方の割り当て数。remain - i はもう一つの差し込み口に割り当てる数。 9 if remain - i == i then #両方の差し込み口に同じ数が割り当てられる場合、差し込み口を入れ替えただけのものを重複してカウントしないようにする。 10 cnt += set_tap(i) * (set_tap(i) + 1) / 2 11 else #そうでない場合、そのまま両方の組み合わせの積をカウント。 12 cnt += set_tap(remain - i) * set_tap(i) 13 end 14 } 15 # 3口 16 (1..(remain / 3)).each{|i| #3口の差込口のうち、一番小さい数を割り当てる物の割り当て数。残りはremain - i。 17 (i..((remain - i) / 2)).each{|j| #残り2口の差し込み口のうち、小さい数を割り当てるの割り当て数。残りはremain - (i + j)。 18 #2口の場合と同じく、差し込み口を入れ替えたものを重複してカウントしないようにする場合分け。 19 if (remain - (i + j) == i) && (i == j) then #全部の差し込み口に同じ数を割り当てる場合。 20 cnt += set_tap(i) * (set_tap(i) + 1) * (set_tap(i) + 2) / 6 21 elsif remain - (i + j) == i then # i <= j <= remain (i + j) なので本当はいらない。 22 cnt += set_tap(i) * (set_tap(i) + 1) * set_tap(j) / 2 23 elsif i == j then #小さいほう二つに同じ数を割り当てる場合。 24 cnt += set_tap(remain - (i+j)) * set_tap(i) * (set_tap(i)+1) / 2 25 elsif remain - (i + j) == j then #大きいほう二つに同じ数を割り当てる場合。 26 cnt += set_tap(j) * (set_tap(j) + 1) * set_tap(i) / 2 27 else #3つとも違う数を割り当てる場合。 28 cnt += set_tap(remain - (i + j)) * set_tap(j) * set_tap(i) 29 end 30 } 31 } 32 @memo[remain] = cnt 33end 34 35puts set_tap(N)

投稿2020/01/06 05:21

yudedako67

総合スコア2047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pyxis

2020/01/06 05:50

丁寧な解答ありがとうございました。 理解できました。 それぞれの挿し口の空きの合計が同じ場合は重複組み合わせになっているので、 set_tap(i)種のものから2個選ぶと考えれば重複組み合わせの公式より (set_tap(i)+2-1)C2となり、それをコードにすると set_tap(i) * (set_tap(i) + 1) / 2 同様に3口でそれぞれの口で空き数が同じ場合は (set_tap(i)+3-1)C3なので、それをコードにすると set_tap(i) * (set_tap(i) + 1) * (set_tap(i) + 2) / 6 となっているのですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問