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

数学

今回は2021年までに読んだ
プログラミング関連の本の中で
良かったオススメ本を紹介します。

解説動画はこちら


2021年はアプリで本を管理するようにしました。

今までに読んだ本の中で
良かったものをテーマ別でお送りします。


本を整理するためのアプリは
「ブクログ」
というのを使っています。

本を買ったらこれにバーコードで
登録しています。

その中でカテゴリに分けて管理しているので
テーマ別で4冊ご紹介します。


1.プログラミング

222

プログラミング関連は40冊くらい持っていました。
この中でのオススメは・・・

「アルゴリズム×数学」が基礎からしっかり身につく本
toppage-001

こちらはアルゴリズムが図解で
しっかりと解説されているにもかかわらず
C,C++,Java.Pythonでのコードもgithubに載っており
問題集が200問もついているという
いたれりつくせりな本です。

これから競技プログラミングなどを
学んでいきたいという方には
かなりオススメできる本だと思います。

にしても良くこのクオリティの内容を
まとめ上げたなーという印象で
驚愕しています。

筆者の能力の高さに脱帽の一作です。


2.データサイエンス
スクリーンショット 2022-01-08 15.01.59

データサイエンス関連は20札ほど持っていますが
この中でのオススメは・・・


Kaggleで勝つデータ分析の技術
TH320_9784297108434

2019年発行なので2年ほど前の本ですが
・Kaggleに挑戦したい
・機械学習系のコードを書きたい
・機械学習のテクニックを学びたい
というのにはぴったりの本です。

仕事で使うので多読していました。
おそらくKaggle本の走りで一番最初に
出たんじゃ無いかと思いますが
やはりこれが一番良いなーという印象です。

データサイエンス関連のコードを書く
仕事につきたい方は
これを一冊持っておいても損しないでしょう。


3.統計本
スクリーンショット 2022-01-08 15.53.39

統計学関連の本も
まあまあ持っていますね。

その中でもオススメは・・・

統計学の図鑑

TH320_9784774173313


統計学の本はとにかく分かりづらいのが多いのですが
これは図解が非常に分かりやすくて
概念を理解しやすいと思います。

数式なども載っているのに
分かりやすい本ということで
これから統計学を学ばれる方は
最初に概念を理解するのに役立つと思います。




4.数学
スクリーンショット 2022-01-08 15.02.56

最後に数学関連の本ですが
20冊くらいは持っているようですね。

その中でもオススメは・・・

億万長者だけが知っている教養としての数学
9784478104552

こちらは同名タイトルで他にも出てるみたいでなんで
間違えないようにして下さい。

数学でどうお金を稼げるかについてを
語っている本になっています。

ギャンブルや投資、数学的思考について
かなり詳しく書かれていますが
かなり難しい!!!!

全然理解が追いつかないんですが
自分はこれを読んで
億万長者になったろーって思ってます。

ということで
今回はプログラミングに関わる
おすすめ本をテーマ別で4つ
ご紹介させていただきました。

良ければ
本屋さんで手に取って見てみて下さいね。

それでは。
 

今回は京都大学の数学の入試問題を
プログラミングで解いてみました。

解説動画はこちら


今回の問題は

10進法で表された数 6.75 を2進数で表せ

また、この数と2進法で表された数
101.0101 との積として与えられる数を
2進法 および 4進法で表せ

2段階の問題となっています。

まず2進数に直してみましょう。

bin(数値)
で2進数に直すことができます。
bin(6.75)
TypeError: 'float' object cannot be interpreted as an integer

おっと
エラーが出てしまいました。

binでは整数値しか対応しておらず
小数点値を2進数に直すことができません。

整数部分は簡単に直すことができますが
小数点部分は考えないとダメですね。

小数点の2進数の考え方ですが
小数点第一位から
2分の1 , 4分の1・・・
と言う様に、2の小数点の桁乗で割った数を
基準に考えると直すことができます。

ということで小数点値を2進数に直す関数を
作ってみました。
from decimal import Decimal

def float2bin(num):
    a,b,i= [],[],0 
    n = int(num)
    f = Decimal(str(num)) - Decimal(str(n))
    while n !=0:
        a.append(str(n % 2))
        n = n // 2
    a.reverse()
    while (f != 0):
        t = f * 2
        b.append(str(int(t)))
        f = t - int(t)
        i +=1
        if i >= 10:
            break
    return float('{0}.{1}'.format(''.join(a),''.join(b)))

さてこれで求められるハズです。
10進法で表された数 6.75 を2進数の答えは
print(float2bin(6.75))
110.11

2分の1 + 4分の1 = 0.75
になるので、2進数の小数点部分は
.11ですね。


さて問題にはまだ続きがありますね。

また、この数と2進法で表された数
101.0101 との積として与えられる数を
2進法 および 4進法で表せ

Pythonでは通常の計算は
10進数での計算なので
101.0101 これを10進数に直してあげないと
計算できないですね。

2進数の小数値を10進数に直す
関数を作りましょう。

こんな感じになりました。
from decimal import Decimal

def bin2float(num):
    e,h = [],[]
    n = int(num)
    f = Decimal(str(num)) - Decimal(str(n))
    t = [s for s in str(n)[::-1]]
    for i,s in enumerate(t):
        e.append(int(s)*(2 ** i))
    a = sum(e)
    g = [s for s in str(f)[2:]]
    for i,s in enumerate(g,start=1):
        h.append(int(s)*(1/(2 ** i)))
    b = sum(h)
    return a+b
これで2進数を10進数に直します。

2進数 101.0101の10進数は
print(bin2float(101.0101))
5.3125

10進数同士の積は
6.75 * 5.3125
35.859375

楽ですね。


6.75との積の2進数は?
print(float2bin(6.75 * bin2float(101.0101)))
100011.110111

はい出ました、めちゃくちゃ面倒くさい

問題の最後は
4進数に直した答えも出せ
でしたね。

10進数を4進数に直す関数も
作りましょう。

中身を2から4に変えただけです。
from decimal import Decimal

def float4bin(num):
    a,b,i= [],[],0 
    n = int(num)
    f = num - n
    while n !=0:
        a.append(str(n % 4))
        n = n // 4
    a.reverse()
    while (f != 0):
        t = f * 4
        b.append(str(int(t)))
        f = t - int(t)
        i +=1
        if i >= 10:
            break
    return float('{0}.{1}'.format(''.join(a),''.join(b)))
これで4進数の答えが出せますね

6.75との積の4進数は?
print(float4bin(6.75 * bin2float(101.0101)))
203.313

はいできました。

かなり面倒くさいですね。
小数点値も計算できるライブラリが
実装されることを願いたいところですね。

今回は小数点値の2,4,10進数の計算という
面倒くさい計算をすることとなりました。

数値計算もプログラミングであれば
比較的簡単ですかねーーー

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

今回は受験シーズンにつき
大学の入試問題をプログラムで解いてみました。

解説動画はこちら

 
一橋大学さんの2021年の数学の入試問題です。

問題は

1000以下の素数は250個以下であることを示せ

さて
私は偏差値20位しかないので
普通に解いたら解くことはできません。

プログラムの力を使って
強引に解いていきましょう。

まずは1000以下の素数を列挙したれば
個数がわかります。

素数を列挙してみましょう。

素数を判定するプログラムを書いても良いですが
面倒くさいのでライブラリを用います。

Python言語ではsympyという数学計算ライブラリがあり
それを用いると計算はめっちゃ楽です。

isprimeという素数かどうかを判定する
メソッドがあるのでそれを用いましょう。

素数だと判定された数を
リストに格納して個数を数えます。
from sympy import isprime

prime=[]
for i in range(1,1001):
    if isprime(i):
        prime.append(i)
print(len(prime))
168

ということで
250個以下であるということは
わかりましたね。


でもこれだと素数を列挙して
答案用紙に書くのは大変そうですね。

別の解き方も考えてみましょう。

​素数の倍数を挙げ
それが750個以上出れば
素数は250個以下であることが
示ると思います。

それでは素数の小さい方から
その倍数の個数を数えていきましょう。

2が一番小さいですね。
2の倍数で考えると2は素数なので候補ではなく
4から1000まで数えることになります。

こんな感じで2の倍数のリストを
作ることができます。
bai_2 = [i for i in range(4,1001,2)]
len(bai_2)
499

2の倍数の時点で499個ですね
次は3の倍数です。
bai_3 = [i for i in range(6,1001,3)]
len(bai_3)
332

5の倍数もいきましょう。
bai_5 = [i for i in range(10,1001,5)]
len(bai_5)
199

この時点でリストの個数は
len(bai_2 + bai_3 + bai_5)
1030

ということで、リストの個数は
1000個を超えましたが
このリストは重複が含まれています。

プログラム言語では重複を排除する方法として
セットというデータ型を使うことができます。

pythonでは set() でリストをセットに変換できます。
sets = set(bai_2 + bai_3 + bai_5)
print(len(sets))
731


重複を排除したら731個になってしまいました。

7の倍数も追加してみましょう。
bai_7 = [i for i in range(14,1001,7)]
print(len(bai_7))

sets = set(bai_2 + bai_3 + bai_5 + bai_7)
print(len(sets))
141
768

7の倍数を追加すると素数でないものの数が
750を超えました。

ということで素数が250個以下になることが
わかるかと思います。

あとは解答用紙にどう書くかですかね
そこは知りません!!!!!

ということで今回は
数学の問題をプログラムで解いてみました。

プログラムの勉強にはちょうど良い題材ですね。
それでは。

ドラクエのシミュレーター時間かかるので
簡単なやつですんません。

パスカルの三角形をご存知でしょうか?
結構綺麗な図表ができますよねーー

逆三角的なやつも考えてみました。
頭の体操にはもってこいの題材ですね。

解説動画はこちら




さてパスカルの三角形は
Wikipediaによると
download

まず最上段に 1 を配置、それより下の段は両端には 1 
それ以外の位置には右上の数と左上の数の和を配置

上から n 段目、左から k 番目の数は、二項係数に等しいんだそうです。

download-1

この値は階乗などから計算することができます。

Pythonではmath.factorialで階乗を求められるので

nCk の部分は
math.factorial(n)//(math.factorial(k)*math.factorial(n-k))

と言うように書けるかなと思います。

さて
それでは三角形を描くコードを考えてみましょう。

段数を先に決めて、その段数分計算しましょう。
import math

size = 10
for row in range(1,size+1):
    for k in range(1,row+1):
        n = row-1
        k = k-1
        res = math.factorial(n)//(math.factorial(k)*math.factorial(n-k))
        print(res,end='    ')
    print()
1    
1    1    
1    2    1    
1    3    3    1    
1    4    6    4    1    
1    5    10    10    5    1    
1    6    15    20    15    6    1    
1    7    21    35    35    21    7    1    
1    8    28    56    70    56    28    8    1    
1    9    36    84    126    126    84    36    9    1    

数値は1から始まり
上の段の分を足すと下の数値になっていますね。

まあ、print関数は左から出力してしまうので
綺麗な三角形にはなりませんねーーー

無理くり三角形ぽくしたのが次のコードです。

先に下の段の長さを測っておき
その長さに中央揃えして無理やり三角形を作ります。
import math

size = 12
ms = ['{0} '.format(math.factorial(size-1)//(math.factorial(k-1)*math.factorial(size-k))) for k in range(1,size+1)]
ln = len(' '.join(ms))
for row in range(1,size+1):
    ps = ['{0} '.format(math.factorial(row-1)//(math.factorial(k-1)*math.factorial(row-k))) for k in range(1,row+1)]
    result = ' '.join(ps)
    print(result.center(ln, ' '))
                         1                         
                       1  1                        
                      1  2  1                      
                    1  3  3  1                     
                   1  4  6  4  1                   
                1  5  10  10  5  1                 
              1  6  15  20  15  6  1               
            1  7  21  35  35  21  7  1             
          1  8  28  56  70  56  28  8  1           
       1  9  36  84  126  126  84  36  9  1        
   1  10  45  120  210  252  210  120  45  10  1   
1  11  55  165  330  462  462  330  165  55  11  1 

ムフウ、ちょいといびつではありますが
ぽくはなってるんじゃないでしょうか。

これを綺麗に並べるにはもう少し努力が必要ですね!!

さて
今度は逆を考えてみましょう。

さっきのは上からどんどん大きくなってきました。

今度は下から上に向かって大きくなる三角形を
考えたいと思います。

先に下の段の数値を指定しておくと
上の段に向かって足し上げていき
一番上は1個になります。

コードは次のようになりました。
q = [21,35,76,56,23,46,19]
answer=[q]
for j in range(len(q)-1):
    tmp = []
    for i in range(len(answer[j])-1):
        tmp.append(answer[j][i]+answer[j][i+1])
    answer.append(tmp)
answer.reverse()
for num in answer:
    print()
    print('    '.join(map(str, num)))

3131 1677 1454 864 813 641 410 454 359 282 167 243 211 148 134 56 111 132 79 69 65 21 35 76 56 23 46 19

あまりコードは綺麗ではないですが
ちゃんと下から上に向かって足されていってますね。

こう言うプログラミングは
頭の体操にはとっても良い気がします。

プログラミングがなかなか上達しない
そんな方はこう言う問題を解くコードを
いろいろ試してみると良いかもしれません。

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

ガチャなどの身近に有る確率の問題を解いてみました。


解説動画はこちら



初めに簡単な問題を

問1. 30人クラスで同じ誕生日の生徒がいる確率は?

考えたい方はここでスクロールを止めて
考えてみてください。








答え:

1組みでも誕生日が一致している確率
= 1 - 全員の誕生日が異なる確率

となるので

計算式は
スクリーンショット 2020-05-24 15.50.12


こんな感じになります。

計算めんどくさいのでPythonで解きます。
from functools import reduce
from operator import mul

n = 30
a1 = [i for i in range(365,365-n,-1)]
a2 = [365 for i in range(n)]
print('{:%}'.format(1- reduce(mul, a1)/reduce(mul, a2)))
70.631624%

出ました。

30人クラスだと7割くらいの確率で
1組みくらいは同じ誕生日の人が居そうですね。

50人までの確率を調べてみましょう。
from functools import reduce
from operator import mul
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(16,5))
x,y = [n for n in range(2,51)] , []
for n in x:
    a1 = [i for i in range(365,365-n,-1)]
    a2 = [365 for i in range(n)]
    y.append(1-reduce(mul, a1)/reduce(mul, a2))

plt.bar(x,y)
plt.grid()
plt.show()
download

20人クラスでも40%ほど
50人クラスでは97%にもなります。

だいたい会社だと100人超えてくると思うので
1組みは同じ誕生日の人がいるんじゃないかと思います。

問2. 当たる確率1%のガチャ
100回連続で外れる確率は確率は?

ガチャの確率の問題です。

自分もよくガチャ引きますが
3倍祭りとか言ってるのに
全然当たらないですよねーーーー






答えは

100連続で外れる確率
1回引いて外れる確率の(100乗)

 と言うことで
計算は簡単です。

print('{:%}'.format((99/100)**100))
36.603234%

ガチャ確率1%だと
100回引いても3割以上は1回も当たらないんです!!

では
どれくらい引いたら当たるかを
シミュレーションしてみましょう。

当たる確率は 1-外れる確率 で計算できますね。

500回引いた際の当たる確率のシミュレーションです。
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(16,5))
x,y = [n for n in range(1,501)],[]
for n in x:
    y.append(1-(99/100)**n)

plt.bar(x,y)
plt.grid()
plt.show()
download-1

100回引いた程度だと36%も外れるわけですから
もっと引かないと当たらないですよね。

500回ほど引けばほぼ当たりそうな感じはしますが
確実ではないと言うところがミソです。

本当に1%の確率で当たるのか??
確率はちゃんと1%なのか??

と言う疑問を解消するには
1万人ほどアンケートとってみるしか
ないんじゃないかと思います。

1%の確率で当たるガチャを100回引く
1万人ガチャシミュレーションの結果は・・・

import random

weights = [0.99,0.01]
d = {b:0 for b in range(101)}
for p in range(10000):
    a = 0
    for i in range(100):
        tf = random.choices([False,True], weights=weights)[0]
        if tf:
            a=i+1
            break
    d[a]+=1
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(16,5))
x = list(d.keys())
y = list(d.values())

plt.bar(x,y)
plt.grid()
plt.show()
download-2

0は外れた人の数、それ以外は当たった回数で何人いるかです。

0のところは先ほどの確率で行くと36%=3600人ほど
外れているのが分かります。

1万人くらいのアンケートをとって
外れてる人が何人いるのかを調べれば
本当にガチャの確率が1%だったのかを
検証できるかもしれませんね。


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


このページのトップヘ