乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

受験

さて、今年も受験シーズンですねーー

数学の問題をプログラムで解くという
無駄なことをやっていきたいと思います。

解説動画はこちら





2020年の数学1の問題です。

さてまず最初は

スクリーンショット 2020-01-26 16.48.25

問題見るだけだと
なんだかよく分からないですよねー

まずはこの直線L君を作図してあげましょう。
そうすれば分かりやすくなるはずです。

Python言語では
数学的な作図が非常に楽です。

数式用、作図用のライブラリを
インポートします。
import numpy as np
import warnings
import matplotlib.pyplot as plt
warnings.simplefilter('ignore')
%matplotlib inline

作図では直線を描くので
x軸、y軸の値が必要になります。

y軸の値は数式を元に生成されるので
x軸の値を適当に生成します。

numpyのlinspaceで
数値を適当に生成することができます。
a=1
x = np.linspace(-5, 5, 100)
y = (a**2-2*a-8)*x + a
plt.plot(x, y,label=str(a))
plt.legend()
plt.show()
download



aが1の時を作図してみると
傾きは右肩下がりで負の値になっています。

今度はaが8の時を試してみます。
download-1

そうすると右肩上がりで
傾きが正の値になりました。

どこかに境界線があるはずです。

aの値を複数用いて
まとめて作図してみます。
for a in range(1,6):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    plt.plot(x, y,label=str(a))

plt.legend()
plt.show()
download-2
aの値を1から5までで作図すると
4の時に平行になったように見えます。
5になると正の値になっているように見えます。

と言うことで4が境界線に
なっていると言えそうです。

ただし、aの値は負の値を取ることも
考えられますので、aが負の時も
作図してみます。
for a in range(-6,0):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    plt.plot(x, y,label=str(a))

plt.legend()
plt.show()
download-3
aが負の場合
-2で平行になっているように見えますね。

合わせると回答すべきポイントは
アイ< a < ウなので

-2 < a < 4

と言う感じになりますね。

ただしこれだと
図からの判断なので
実際に傾きを求めてみましょう。

傾きは
スクリーンショット 2020-01-26 16.48.46
にて求めることができます。

x,yの共分散は
np.cov(x,y)[0,1]

xの分散は
np.var(x)

これで求めることができます。

これでaを入れて傾きを求めてみると
for a in range(-5,6):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    
    # 傾き = x,yの共分散 / xの分散
    coef = np.cov(x,y)[0,1] / np.var(x)
    print(a,'\t',coef)
-5 	 27.27272727272728
-4 	 16.161616161616156
-3 	 7.070707070707068
-2 	 0.0
-1 	 -5.050505050505051
0 	 -8.080808080808078
1 	 -9.090909090909092
2 	 -8.080808080808078
3 	 -5.050505050505051
4 	 0.0
5 	 7.070707070707068




はい、これで-2と4のところで
ちゃんと傾きは0で平行になっていますので
それを越えれば傾きが正になることが確認できました。


続いて

スクリーンショット 2020-01-26 16.48.55

先ほどの直線Lとx軸の交点と言っているので
x軸を作図してあげれば
交点bが分かりやすくなります。

プログラム上では
単純にy=0とし、xを適当な値で結んで
直線を描けばx軸になります。
a=1
x = np.linspace(-5, 5, 100)
y = (a**2-2*a-8)*x + a

# 直線Lを作図
plt.plot(x, y,label=str(a))
# x軸を作図
plt.plot([-5,5], [0,0],c='red')
plt.legend()
plt.show()
download-4

はい、赤線と青線の交わる部分が交点bですねー

さてaが変われば傾きが変わり
交わる部分も変わってくるのですが

求めたい交点bについては
yの値は0なのでxの値を求めてあげれば
良いと言うことになります。

と言うことで
直線の式を変形して
xの値を求めてみましょう。

式を変形すると
y = (a**2-2*a-8)*x + a

y-a = (a**2-2*a-8)*x

(y-a)/(a**2-2*a-8) = x

x = (y-a)/(a**2-2*a-8)
y=0なので、yの部分を0に変えてあげれば
xの値をすぐに求めることができますね。

aを変えてxを求めてみましょう。

まずは a > 0 の場合

for a in range(0,11):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print(a,'\t',x)
0 	 -0.0
1 	 0.1111111111111111
2 	 0.25
3 	 0.6
5 	 -0.7142857142857143
6 	 -0.375
7 	 -0.25925925925925924
8 	 -0.2
9 	 -0.16363636363636364
10 	 -0.1388888888888889

これでみると4のところが無くて
5から負の値になっていますね。

4の値をもう少し細かく見てみましょう。
for a in np.linspace(3.9, 4.1, 21):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print('{:.03}'.format(a),'\t',x)
3.9 	 6.610169491525415
3.91 	 7.3510058281631725
3.92 	 8.277027027027007
3.93 	 9.467598169115819
3.94 	 11.054994388327726
3.95 	 13.277310924369653
3.96 	 16.610738255033528
3.97 	 22.166387493020434
3.98 	 33.277591973244355
3.99 	 66.6110183639381
4.01 	 -66.72212978369552
4.02 	 -33.38870431893784
4.03 	 -22.277501381979285
4.04 	 -16.72185430463576
4.05 	 -13.388429752066106
4.06 	 -11.166116611661277
4.07 	 -9.578724405742625
4.08 	 -8.388157894736848
4.09 	 -7.462141944900592
4.1 	 -6.721311475409841

と言うことで4を越えると
xが0未満になり成立しません。

a > 0 の場合、b > 0となるのは エ < a < オ
なので

答えは

0 < a < 4

ですね。


a<=0の場合は
for a in range(-10,0):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print(a,'\t',x)
-10 	 0.08928571428571429
-9 	 0.0989010989010989
-8 	 0.1111111111111111
-7 	 0.12727272727272726
-6 	 0.15
-5 	 0.18518518518518517
-4 	 0.25
-3 	 0.42857142857142855
-1 	 -0.2

-2で負の値に切り替わりますね。
-2付近を細かく見ると
 for a in np.linspace(-3, -1, 21):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print('{:.03}'.format(a),'\t',x)
-3.0 	 0.42857142857142855
-2.9 	 0.466988727858293
-2.8 	 0.5147058823529413
-2.7 	 0.5756929637526651
-2.6 	 0.6565656565656565
-2.5 	 0.7692307692307693
-2.4 	 0.9375000000000004
-2.3 	 1.2169312169312176
-2.2 	 1.774193548387094
-2.1 	 3.4426229508196755
-1.9 	 -3.220338983050848
-1.8 	 -1.551724137931033
-1.7 	 -0.9941520467836252
-1.6 	 -0.7142857142857142
-1.5 	 -0.5454545454545454
-1.4 	 -0.4320987654320987
-1.3 	 -0.3504043126684635
-1.2 	 -0.28846153846153844
-1.1 	 -0.2396514161220043
-1.0 	 -0.2

はい、これで-2を界に
負の値になることが確認できたので

a <= 0 の場合、b > 0となるのは a < カキ

a < -2

となりました。

ふう
普通に問題を解くのに比べて
3倍ほどは時間がかかるんじゃないでしょうかね

このように
無駄なコードを書くことで
プログラミングを上達させることが
できるかもしれません。

お暇であれば試してみるのもいかがでしょうか?

今回はこれまでです。
それでは

今回はヒマなので
なーんか面白いことないかーとか
思っていましたら

東の方に
有名な大学が有るらしいんですよ。


東京大学って知ってます?

自分は行ったことないんで
よく知らないんですけど
有名大学みたいなので
数学の問題を解いてみることにしました。

解説動画はこちら



2003年の東大の入試問題より
 
円周率が 3.05 より大きいことを証明せよ。


さて
どう解きましょうかねー

とりあえず
円周の長さ > 内接X角形の外周

になるはずなので
この内接する多角形の外周を求めて
それが3.05よりも大きければ

総じて
円周率> 3.05 
となるのではないかと思います。

まずは円を描いてみましょう。

Pythonのライブラリを用いて
円を描いてみます。

# ライブラリの読み込み
import numpy as np
import matplotlib.pyplot as plt
% matplotlib inline


描画用のライブラリとして
matplotlibを読み込んでおきます。

# 半径5の円を描く
plt.figure(figsize=(2,2),dpi=300)
r = 5
x = [np.sin(np.radians(_x))*r for _x in np.linspace(-180,180,361)]
y = [np.cos(np.radians(_y))*r for _y in np.linspace(-180,180,361)]
plt.plot(x,y)

plt.xticks([i for i in range(-r,r+1)])
plt.yticks([i for i in range(-r,r+1)])
plt.axes().set_aspect('equal','datalim')
plt.show()
en1
円を描くにはx,y座標が必要ですが
その点を求めるのにnumpyを使います。

numpy.linspaceで等間隔の配列が作成できます。

あとはx,y座標の点を求めるのに
numpy.sin , numpy.cos を使います。
sin , cos に渡せるのはradianでないといけないので
一旦numpy.radiansで変換します。

これで円を描くことができます。


次にこの円に内接する多角形を描いてみましょう。
今回は12角形を描くこととします。

わかりやすくするために
三角形の辺の長さが3:4:5になるという法則を用いて
座標を決めていきます。

一番長い5の辺は円に接する先端ですね。
あとはx,yの座標は3か4になります。

一番上をx=0,y=5として座標点をプロットしてあげます。
# 内接する12角形を描画
plt.figure(figsize=(2,2),dpi=300)
r = 5
x = [np.sin(np.radians(_x))*r for _x in np.linspace(-180,180,361)]
y = [np.cos(np.radians(_y))*r for _y in np.linspace(-180,180,361)]
plt.plot(x,y)

x2 = [0,3,4,5,4,3,0,-3,-4,-5,-4,-3,0]
y2 = [5,4,3,0,-3,-4,-5,-4,-3,0,3,4,5]
plt.plot(x2,y2)

plt.xticks([i for i in range(-r,r+1)])
plt.yticks([i for i in range(-r,r+1)])
plt.axes().set_aspect('equal','datalim')
plt.show()
en2
はいこれで円に接する12角形が描けました。

あとはこの辺の長さを求めて
あげれば良いということになります。

辺の長さを求めるにはどうすれば良いでしょうか?

これはnumpyを使って
ユークリッド距離を求めることで
辺の長さを計算することができます。

numpy.linalg.norm(座標a - 座標b)

これで2点のユークリッド距離を求めることができます。

12角形のうち
全部を求める必要はなく

右上部分の4点を用いて
3辺の長さを計算してみましょう。

x,y座標はそれぞれ
0,5
3,4
4,3
5,0
となるので
そのうち2点を使って計算します。
l = np.array([[0,5],[3,4],[4,3],[5,0]])
c1 = np.linalg.norm(l[0]-l[1])
c2 = np.linalg.norm(l[1]-l[2])
c3 = np.linalg.norm(l[2]-l[3])
ans1 = 4 *(c1+c2+c3)
print(ans1)

30.9550755308

さてこれで多角形の辺の長さが計算できました。

問題文は3.05より大きいことを証明せよなので
比率を合わせます。

この円は半径5の円なので直径は10です。
なので10倍します。
30.5


10倍した30.5よりも
12角形の外周は30.95のため大きくなり

必然的にそれよりも円周は大きいので

円周率>3.05

になるはずです。

さて
描画などについては
Pythonのライブラリを用いると簡単に
描くことができ
また座標間の距離なども
簡単に計算することができます。

数学の問題では
このnumpyとmatplotlibライブラリを使って
いろいろ問題に応用することができます。

数学的な可視化や
その計算については
numpyやmatplotlibライブラリを
用いることが多いです。

単純な計算については
通常のPythonプログラムだけでも
行うことができます。

問題を読み間違わなければ
このくらいの計算は
プログラミングで簡単に行うことができるので

プログラミングを覚えていない方
これから覚えたい方は
ぜひPythonを覚えてみてください。

Pythonについては無料の動画講座を用意しています。
乙py式5時間で学ぶプログラミング基礎(python編)

興味のある方はぜひこちらをご参照くださいませ。

それでは。

はいどーもー
乙pyです。

暇なので受験シーズンだし
有名中学の算数の問題をプログラムで解けないかと
思ったりしたわけですね。

インターネットの
Googleで調べたんですが
そこで問題が難しそうな学校を見つけてしまったんですね。

皆さん

難しい中学校って知ってます?

自分は近所の中学校に行ったので
知らないんですけど
問題を解いてみたんですねー

というものの第八弾です。


動画解説はこちら



始めの問題
有名中学の入試問題.014


これも一瞬で解けます。
for i in range(1,20):
    st = '2' + '0'*i +'7'
    if int(st)%27==0 and int(st)%81!=0:
        print(st)
        break
先頭が2で末尾が7、間が0という
条件を充しつつ
27で割り切れて、81で割り切れないというものが
1つでも見つかった時点で
プログラムは終了です。

条件の書き方もそこまで難しくはないと思います。


次の問題は
有名中学の入試問題.013

まずは図を把握しろという
難問でした。

図のルールがわかれば
プログラムに落とし込むのは容易です。

要は日本語の問題ですね。
きちんと仕様を理解するというのが
問題のポイント(笑)です。

1のコードは
num = [1,2,3,4,5,6,7,8,9,10,11,12]
q1 = []
for i in range(1,6):
    q1 = num
    num = [num[0],num[6],num[1],num[7],num[2],num[8],num[3],num[9],num[4],num[10],num[5],num[11]]
print(q1)

1回目は入れ替えが起きないので
入れ替えが始まるのは2回目からですね

その分を考慮する必要があります。

一旦数字の並びは
リストに格納し、その並びを求めることとしました。

2のコードは
num  = [1,2,3,4,5,6,7,8,9,10,11,12]
num1 = [1,2,3,4,5,6,7,8,9,10,11,12]
q2 = 1
for i in range(1,13):
    q2 +=1
    print(i,num)
    num = [num[0],num[6],num[1],num[7],num[2],num[8],num[3],num[9],num[4],num[10],num[5],num[11]]
    if num == num1:
        print(q2)
        break

元の数値の並びを置いておき
入れ替えをするものと並びを比較してあげるだけの
シンプルなコードです。

12個しか数字がないので
元と同じになるのに
12回以上は入れ替えが起きないだろうと
試行回数は減らしています。

3のコードは
num = [1,2,3,4,5,6,7,8,9,10,11,12]

for i in range(2006-1):
    num = [num[0],num[6],num[1],num[7],num[2],num[8],num[3],num[9],num[4],num[10],num[5],num[11]]
print(num)

単にその回数実行してあげるだけ
ただし、その回数には注意が必要ですね。


図で考えると
すごく難しそう

でもプログラムはシンプル

そんな問題でした。

この問題の図を作るのに
一番時間が掛かるという・・・・

解くのは一瞬

はいどーもー
乙pyです。

暇なので受験シーズンだし
有名中学の算数の問題をプログラムで解けないかと
思ったりしたわけですね。

インターネットの
Googleで調べたんですが
そこで問題が難しそうな学校を見つけてしまったんですね。

皆さん

難しい中学校って知ってます?

自分は近所の中学校に行ったので
知らないんですけど
問題を解いてみたんですねー

というものの第七弾です。


動画解説はこちら



まず始めの問題は

有名中学の入試問題.004

整数を文字にして連結していき
そこに出てくる1の個数を数えればいいだけの
シンプルな問題です。

コードは

ans1=''
for i in range(1,101):
    ans1+=str(i)
print(len(ans1))
ans2=''
for i in range(1,1001):
    ans2+=str(i)
print(len(ans2))
print(ans2.count('1'))

100回だろうと、1000回だろうと
コードの量はほとんど変わりません。

次の問題


有名中学の入試問題.003

細胞B,T

それぞれ1秒毎に
BはBとT
TはBとB

個数としてはどちらも
2倍に増えていますねーー

ということは
8秒後には2 ** 8
倍になっていそうですね

あとはその配分がどうなっているかということ

動画では初めはB
その後BとTの時で処理を分けて増やし
最後にどうなったのかを出力するようにしました。

sells = ['b']
for i in range(8):
    tmp = []
    for sell in sells:
        if sell =='b':
            tmp+=['b','t']
        else:
            tmp+=['b','b']
    sells=tmp

print(sells.count('b'))
print(sells.count('t'))

当初の目論見通り
BとTで足すと256個でしたね。

TとTで

TT兄弟

なんちゃって


はいどーもー
乙pyです。

暇なので受験シーズンだし
有名中学の算数の問題をプログラムで解けないかと
思ったりしたわけですね。

インターネットの
Googleで調べたんですが
そこで問題が難しそうな学校を見つけてしまったんですね。

皆さん

難しい中学校って知ってます?

自分は近所の中学校に行ったので
知らないんですけど
問題を解いてみたんですねー

というものの第六弾です。


動画解説はこちら



始めの問題
有名中学の入試問題.018


はい
非常に簡単な問題です。

1,2,3同時に答えを導くことができます。

コードは

ans1,ans2,ans3 = [],[],[]
for i in range(100,1000):
    if i%3==0 and i%111!=0:
        ans1.append(i)
    if i%2==0 or i%11==0:
        ans2.append(i)
    if (i%2==0 or i%11==0) and (i%3==0 and i%111!=0):
        ans3.append(i)
3桁の整数の分だけforループで1つ1つ調べて行くだけですね。
割り切れる と 割り切れない
の複合条件になるところがポイントでしょうか?

条件の優先順位のつけ方と and  or などの使い方が
問題を解くキーになりそうですね。


次の問題
有名中学の入試問題.016

この問題は2段構えとしました。

まずは
A,B,Cの仕事量がどのくらいになるのかというのを
計算しておかないと

ABCを1台づつ使った際の仕事量が計算できないです

from  itertools  import product

for a,b,c in product(range(1,10),range(1,10),range(1,10)):
    if (a+b)*35 == (a+b*3)*20 and (a+b)*35 == (a*2+c*5)*14:
        print(a,b,c)
        break

これでABCの仕事量が計算できました。
A:5
B:3
C:2

あとは元々
AとBで35日かかった仕事なので
ABCでX日掛かるかを求めれば良いだけですね。

a,b,c = 5,3,2
for x in range(35):
    if (a+b+c)*x == (a+b) *35:
        print(x)
        break

さすがに簡単すぎて
皆さん飽きてきましたよね?

プログラミング向きの
歯ごたえのある問題があると
いいですねーーー

まだまだ続きます


このページのトップヘ