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

More than 5 years have passed since last update.

2015年度東大理系数学第5問をpythonでどうしても解きたい。

Last updated at Posted at 2019-02-01

#はじめに
2019年も1ヶ月が過ぎ受験シーズンに迫ってきた。
Atcoderを始めて2週間が過ぎ、毎日過去問をあさる日々。
せっかく受験シーズンに迫ってきたので競プロの基本問題っぽい受験数学の問題をpythonで解きたいと思った。

ぱっと思いついたのが僕が大学受験をした年のあの有名な2015年度東大理系数学の第5問。

mを2015以下の正の整数とする。{}_{2015}C_mが偶数となる最小のmを求めよ。

これをpythonで解いてみよう。

受験会場では合否を分けた1問だったらしい。

ちなみに答えはm = 32

#解

組み合わせ(combination)は


{}_nC_r = \frac{n!}{(n-r)!r!}

で表される事を用いてまず自分なりに書いてみる。

まあ上の式をそのままぶちこむだけ。

2015_C_m
def f(n):
    if n == 0:
        return 1
    else:
        return n * f(n - 1)

for i in range(1, 2016):
    if f(2015) / (f(2015 - i) * f(i)) % 2 == 0:
                  print(i)
                  break
else:
                  print("no answer")

###結果1

RecursionError: maximum recursion depth exceeded in comparison

のエラーが出た。

問題に対する解として間違ったコードは書いていないつもりだったが、どうやら再帰の回数の上限を超えてしまったらしい。

再帰の上限のデフォルトは1000とのこと。
このコードだと2000回以上再帰しているのでアウト。

これを解消するために

sys.setrecursionlimit(limit)

これで再帰の深さを設定する。


これらを考慮してもう一度書いてみた。
2015_C_m
import sys
sys.setrecursionlimit(3000)

def f(n):
    if n == 0:
        return 1
    else:
        return n * f(n - 1)

for i in range(1, 2016):
    if f(2015) / (f(2015 - i) * f(i)) % 2 == 0:
                  print(i)
                  break
else:
                  print("no answer")

最初の2行以外何も変えていない

###結果2

6

!?

間違ってる。(答えは32)

いろいろ調べた結果、型変換による精度落ちから誤差が生じているせいらしい。


11行目の

if f(2015) / (f(2015 - i) * f(i)) % 2 == 0:

の箇所でint型からfloat型へ型変換が行われ、floatの仮数部(23ビット)よりint(32ビット)の方が大きいため値によっては精度落ちが発生してしまうらしい。
参照:http://akademeia.info/index.php?%A5%B3%A5%F3%A5%D4%A5%E5%A1%BC%A5%BF%A4%CE%B8%ED%BA%B9

実際、このコードで2015_C_6を計算してみると
92274542271702512
という値になる。

正しくは、
92274542271702505 
(https://keisan.casio.jp/exec/system/1161228812 による計算結果)

の為、+7だけ誤差が生じている。


他にもいろいろな種類の誤差があるらしいが、極端に大きいまたは小さい値や、それら同士の演算の際には誤差が生じるケースがあることを頭に入れておいて間違いなさそうだ。

とりあえず整数値だけを扱うために11行目を

if f(2015) // (f(2015 - i) * f(i)) % 2 == 0:

に変えてあげて実行してみると
###結果3

32

となり所望の結果が得られた。





いや待てよ、
誤差が怖いなら素直にやるより、もっと小さな値だけの演算だけを使うコードを書けばいいじゃないか。

#解2

2015_C_m
def f(n):
    li = []
    while True:
        for i in range(2, n+1):
            if n % i == 0:
                n = n // i
                li.append(i)
                break
            else:
                continue
        else:
            break
    return li

for i in range(1, 2016):
    a = 0
    b = 0
    for k in range(1, i+1):
        a += f(2016 - k).count(2)
        b += f(k).count(2)
    if a > b:
        print(i)
        break
else:
    print("no answer")

関数f(n)でnの素因数をひとつずつぶちこんだリストを返し、
分子の持つ素因数2の数が、分母の持つ素因数2の数を超えた時点で出力して終了。

結果はもちろん32。

最小に書いたコードが2015!という大きな値を扱うのに対し、このコードは扱う整数値の最大値が2016とかなり小さくなるので、なんか優しげである。


#結論
テスト期間中に考えることではない。(単位やばいです。)

#参考
https://atcoder.jp/
http://d.hatena.ne.jp/aldente39/20110713/1310559234
http://akademeia.info/index.php?%A5%B3%A5%F3%A5%D4%A5%E5%A1%BC%A5%BF%A4%CE%B8%ED%BA%B9
https://keisan.casio.jp/exec/system/1161228812
https://math.nakaken88.com/problem/tokyo-u-r-2015-5/

3
0
5

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