中学受験算数の組み合わせ問題は面倒だ

受験算数めんどくさい

子供の算数の勉強を見ていたら、何というか面倒くさい問題があった。
以下のような問題。(一応、最初に断っておくと、この学校自体を面倒くさいとか言っているわけではないです)

西武文理学園(2010)
4枚のカードA、B、C、Dがあり、それぞれ表と裏に次のように数字が書いてあります。
[0,1], [2,3], [4,5], [6,7]
この中からカード3枚を順に並べて、3桁の整数を作ります。次の問いに答えなさい。

(1)A、B、Cの3枚のカードを順に並べるとき、偶数は何通りできますか。
(2)A、B、C、Dの4枚のカードから3枚を選んで順に並べるとき、3の倍数は何通りできますか。

この(2)が、(自分的に)スカッと解けなくて、なんというか面倒くさい。
解法も考えたんだけど、試験時間とか考慮すると、時間があったらやる系の問題なんだろうなあ、という感じ。こういう問題は面倒がり屋の自分には無理だし、子供には「大変だなあ」と言っておいた。

ちなみに、解くに当たってのポイントは、以下の3つ(のはず)。

  1. 3の倍数は、各位(くらい)の和が3の倍数になる(使える組み合わせに制限がかかる)
  2. カードの表裏があるので使える組み合わせに制限がある(使える組み合わせに制限がかかる)
  3. 3桁の整数なので0が最初に来ることはない(並びに制限がかかる)

最初の2つで探索の範囲が絞られるけど、それでもまだ多いように思う。受験界の算数得意な人たちにとっては「何いってんの?」という感じかもしれないけど。

うちにはパソコンがあるじゃないか

こういうのはコンピュータにやらせりゃ良いんだよ、と大人げない態度をとって、勉強している子供の横で python のコードを書いてみた。
すっかり鈍っていて普通の順列・組み合わせすら怪しかったので、そこから。

VALUES = [0,1,2,3]
NSEL = 3

def eval_data(data):
       print data
       return

def perm(src, dst, is_combi=False):
       # finish one.
       if NSEL <= len(dst):
               eval_data(dst)
               return

       # it has not completed it yet.
       for i in range(len(src)):
               if is_combi and dst and (src[i] <= dst[-1]):
                       continue
               val = src.pop(i)         # remove & save
               perm(src, (dst + [val]), is_combi)
               src.insert(i, val)       # restore

if __name__ == "__main__":
       print 'Permutation ---------'
       perm(VALUES, [])
       print 'Combination ---------'
       perm(VALUES, [], is_combi=True)

これで、順列と組み合わせはOK。
次は、試験問題の方をやる。要は表裏のペアを入れ込めば良いだけなので、簡単。本当は class card とか作った方が良いんだろうけど、そこは適当。

CARD_DECK = [[0,1], [2,3], [4,5], [6,7]]
NSEL = 3
VERBOSE = False

def eval_data(cards):
       #print cards
       if 0 == cards[0]:
               if VERBOSE: print 'x => ', cards
               return
       total = sum(cards)
       if 0 == (total % 3):
               print 'o => ', cards
       else:
               if VERBOSE: print '- => ', cards

def perm(src, dst):
       # finish one.
       if NSEL <= len(dst):
               eval_data(dst)
               return

       # it has not completed it yet.
       # -> select card
       for i in range(len(src)):
               # remove & save
               card = src.pop(i)
               # -> select face/back of card
               for j in range(len(card)):
                       perm(src, (dst + [card[j]]))
               # restore
               src.insert(i, card)

if __name__ == "__main__":
       perm(CARD_DECK, [])

実行した結果は以下のとおりで、正解した模様。

bash-3.2$ python ./card.py | wc -l
      52

やってることは力ずくだけど、実際の数も見られるし、やっぱりこういうのはコンピュータの仕事だよなあと。
ちょっと嬉しくなって「ほら、どうだ。」と実行したところを子供に見せたのだけど、「ふーん」でおしまい。やることてんこ盛りの受験生にとって、解き終わった問題なんかどうでもよいのであった。