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

数学

たまには真面目に
数学の問題を解いてみましょう

2023年度の東大の数学の問題です


解説動画はこちら



今回は数学の問題です

手で解いたら解法が分からず
すごく時間が掛かってしまう問題も
プログラミングの力を使えば
近似解を簡単に求める事ができます

という訳で
東大の数学の問題を解いてみましょう





2023年度東大数学文系第3問

問題:
黒玉3個、赤玉4個、白玉5個が入っている袋から
玉を1個ずつ取り出し、取り出した玉を順に1列に12個並べる
ただし、袋から個々の玉が取り出される確率は等しいものとする

(1) どの赤玉も隣り合わない確率 p を求めよ
(2) どの赤玉も隣り合わないとき
どの黒玉も隣り合わない条件付確率 q を求めよ


12個の順列の問題ですね
プログラミングの力を使えば
こういうのもシミュレーション出来ます


考え方

まずは石を用意しましょう
次にその石の並びで隣同士なのかどうかを
チェックする方法を考えてみましょう

次のようなやり方で
石のデータと判定方法を
考える事ができます
# 12個の玉を作って試してみる
import numpy as np

data = ["b"]*3 + ["r"]*4 + ["w"]*5
np.random.shuffle(data)

# リストを結合して文字列にする
st = "".join(data)

# 赤玉が隣り合わないかどうか
res1 = "rr" not in st
# 上記のに加えて黒玉が隣り合わないかどうか
res2 = res1 & ("bb" not in st)

print(st)
print(res1)
print(res2)
rwrwwbbrbwwr
True
False

データは文字列やリスト型で定義しておくと
後で計算しやすいです

これを元にして
計算を行ってみましょう



順列を生成してチェックする方法



今回は12個の中から12個を取り出してつくる
順列になるので、その総個数は
import math

math.factorial(12)
479001600


順列は itertools ライブラリの
permutations で作る事ができます

チェック機構では判定式を返し
Trueを1、Falseを0として
条件にあった個数を返していきます

最終的に条件にあった個数と
元の順列の個数を組み合わせれば
確率が求められます
import itertools

def num_check(data):
    st = "".join(data)
    res1 = "rr" not in st
    res2 = res1 & ("bb" not in st)
    return res1, res2

data = ["b"]*3 + ["r"]*4 + ["w"]*5

ans1, ans2 = 0, 0
for t in itertools.permutations(data):
    t1,t2 = num_check(t)
    ans1+=t1
    ans2+=t2

# 総当たりの個数(順列の個数)
permutations_num = math.factorial(12)

print("1の答え : {0}".format(ans1/permutations_num))
print("2の答え : {0}".format(ans2/ans1))
1の答え : 0.2545454545454545
2の答え : 0.6130952380952381




シミュレーションしてみる


総当たりの方法が思いつかなくても
シミュレーションで近似解を求める事ができます

先ほどは順列を作って繰り返しを行っていましたが
単純に回数を指定して、ランダムに並び替えた後に
チェックを行えば、順列の際の近似が求められます

チェック関数は先ほどのを用いるとして
次のような計算でシミュレーションできます

import numpy as np

N = 100000000

ans2_1, ans2_2 = 0, 0
for i in range(N):
    data = ["b"]*3 + ["r"]*4 + ["w"]*5
    np.random.shuffle(data)
    t1,t2 = num_check(data)
    ans2_1+=t1
    ans2_2+=t2

print("1の答え : {0}".format(ans2_1/N))
print("2の答え : {0}".format(ans2_2/ans2_1))
1の答え : 0.25452006
2の答え : 0.613167347202417


小数点第3位くらいまでは
同じ数字になりますね

近似解だと点数は貰えないかもしれませんが
点数関係ない、仕事上の計算であれば
こういったシミュレーションが行えるのが
プログラミングの役に立つポイントです

確率が分からなくても
それっぽい確率を算出する事ができるので
いろいろ便利ですねえ

色々なシミュレーションを行えるので
仕事がめちゃくちゃ捗ります

プログラミング出来ない方は
是非、この機会に覚えてみて下さい


今回は数学の問題を解く
プログラミングでのシミュレーションでした
それでは
 

今回はプロジェクトオイラーの
数学の問題を解いてみました

解説動画はこちら


 
さて
プロジェクト・オイラー

は、英語で書かれた
数学的/コンピューター プログラミングの問題集です
リンクから色々な問題をみる事ができます

レオンハルト・オイラーは
18世紀の数学者・天文学者のことですね

今回はこれを解いていきたいと思います
数学の問題をプログラムを
駆使して解いてみましょう

問題を考えたい人は
動画を止めて考えてみて下さい

解答そのものは動画をご覧ください






問題1

1000未満の3か5の倍数の合計値を求めよ






・・・・・

・・・・

・・・

・・










解答1

比較的簡単な問題です

1000未満の数値の生成
3と5の倍数の判定
合計値を求める

これらが達成出来ていれば
解けると思います

sum([i for i in range(1,1000) if i%3==0 or i%5==0])





問題2

400万未満のフィボナッチ数で

偶数の物の合計値を求めよ

フィボナッチ数とは

1, 2, 3, 5, 8, 13, 21, 34, 55, 89 のように

どの数字も前2つの数字を足した数字のこと




・・・・・

・・・・

・・・

・・



解答2

フィボナッチ数を求める関数を作って
400万までのリストを作り
偶数のものを抜き出せば解けます

フィボナッチ数のリストを返す関数
# その数までのフィボナッチ数を求める
def fib(num):
    a , b , fib_l = 0 , 1 , []
    while b <= num:
        fib_l.append(b)
        a , b = b , a+b
    return fib_l

sum(([f for f in fib(4000000) if f%2==0]))

ジェネレーター式でも同様に出来ます
# ジェネレーター形式
def fib_gen(num):
    a , b = 1 , 1
    while b <= num:
        yield b
        a , b = b , a+b
        
sum(([f for f in fib_gen(4000000) if f%2==0]))




問題3

600851475143 の最大の素因数を求めよ

素因数分解をして、その最大値を求める


・・・・・

・・・・

・・・

・・






解答3

素因数分解をする手法はたくさん有りますが
中でも簡単な試し割り法を使って
素因数分解してみましょう

素因数分解しようとする整数nを
小さい順に割ってみて
割り切れるかどうかを調べる手法です


num = 600851475143

# 3から奇数で割っていく
f , a = 3 , []
while f * f <= num:
    
    # 割り切れたら素因数に採用
    if num % f == 0:
        a.append(f)
        
        # 次の数は、素因数で割った数で探す
        num //= f
    
    # 割り切れなければ次の素数へ
    else:
        f += 2
        
if num != 1:
    a.append(num)

# 素因数の組み合わせ
print(a)

# 素因数の最大値 
print(sorted(a,reverse=True)[0])


なお、ライブラリを用いれば
簡単に素因数分解できてしまいます
# ライブラリで素因数分解
import sympy

num = 600851475143
sympy.factorint(num)





問題4

2 つの 3 桁の数の積から作られる

最大の回文数値を求めよ

101*101 = 10201 のような

回文になる数値の最大値を求める


・・・・・

・・・・

・・・

・・






解答4

数値を文字列に直してひっくり返せば
回文の出来上がりです

answer = {}

for a in range(100,1000):
    for b in range(100,1000):
        s_num = str(a * b)
        if s_num == s_num[::-1]:
            answer[a * b] = [a,b]

for k,v in sorted(answer.items(),reverse=True):
    print(k,v)
    break








問題5

1 から 20 までのすべての数で割り切れる

最小の正の数を求めよ

最小公倍数を求める問題


・・・・・

・・・・

・・・

・・







解答5

最小公倍数は
次の様な計算式で求められます

最小公倍数 = 数値の積 ÷ 最大公約数


最大公約数を求めるには
ユークリッドの互除法を用います

ユークリッドの互除法は
2つの自然数の最大公約数を求める
手法の一つですが
比較的実装が楽です

# ユークリッドの互除法
# 最大公約数を求める
def gcd(a, b):
    if a == 0:
        return b
    m = b % a
    return gcd(m, a)

# 最小公倍数 = 数値の積 / 最大公約数
from functools import reduce

# 最小公倍数を計算する(2つ)
def lcm_base(x, y):
    return (x * y) // gcd(x, y)

# 最小公倍数を計算する(複数)
def lcm(*nums):
    return reduce(lcm_base , nums, 1)


s = [i for i in range(2,21)]
# 最小公倍数をリストで求める際は * を付ける
lcm(*s)



python3.8以降なら
ライブラリでも求められます
# 最小公倍数を求める
import math
s = [i for i in range(2,21)]

# 最小公倍数をリストで求める際は * を付ける
math.lcm(*s)






問題6

100 個の自然数の

和の 2 乗と 2 乗の和の差を求めよ

例1,2,3

(1 + 2 + 3)^2 − (1^2 + 2^2 + 3^2) 

= 36 −14 

= 22



・・・・・

・・・・

・・・

・・






解答6


和の2乗は比較的楽に計算できますが
2乗の和は少しコツがいります

map関数は関数を引数に指定できるので
リストの要素に
関数を適応した結果を求めるのに
すごく役立ちます
nums = [i for i in range(1,101)]

# 和の2乗
sum_square = sum(nums) ** 2
print(sum_square)

# 2乗の和
sum_of_squares = sum(map(lambda n : n**2 , nums))
print(sum_of_squares)

# 2乗の和と和の2乗の差
print(sum_square - sum_of_squares)





問題7

10001 番目の素数を求めよ





・・・・・

・・・・

・・・

・・





解答7

素数を求めるのは流石に面倒くさいので
ライブラリの力を借りましょう

isprimeメソッドは
実行速度がめちゃくちゃ速いので
大抵の人が実装したものよりも
速いだろうと思います
# 素数を求めるライブラリ
from sympy import isprime

primes = [2,3]
for i in range(5,200001,2):
    if isprime(i):
        primes.append(i)
    if len(primes)==10001:
        answer = i
        print(answer)
        break


ライブラリでその数値までの
素数の個数を数える事も出来ます
# ライブラリで素数の個数を数える
from sympy import primepi

print(primepi(answer))







問題8

次の1000 桁の数の中で

最大の積を持つ隣接する13桁は何か

数値は下記をお使いください

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450






・・・・・

・・・・

・・・

・・





解答8


まずは文字列をデータ化しますが
きちんとゴミを取り除いておきましょう

文字列を数値に直して
計算すればOKです

総積を求めるのは
for文を使っても良いですが
reduce関数などを使って
まとめる事ができます


data = '''
73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450
'''.replace('\n','')

import operator
from functools import reduce

answer = {}
for i in range(0 , len(data)-12):
    nums = data[i : i+13]
    if '0' in nums:
        continue
    
    # 総積を求める
    a = reduce(operator.mul, [int(n) for n in nums],1)
    answer[a] = nums
    
for k,v in sorted(answer.items(),reverse=True):
    print('数値 :  {0} , 文字列 : {1}'.format(k,v))
    break







問題9

a < b < c で a + b + c = 1000である時

ピタゴラスのトリプレットとなる数値の

 a * b * c の値を求める

ピタゴラスのトリプレットとは

a^2 + b^2 = c^2






・・・・・

・・・・

・・・

・・




解答9

a<b<c かつ a+b+c=1000 なので
abcの数値の最大値はおのずと決まってきます

あとはピタゴラスの定理のような
関係性になる数値を探し当てれば
それが答えです


# aの最大値は1000の3分の1より小さい
for a in range(1, 332):
    
    # bの最小値は a+1、最大値は 1000から a を引いた半分
    for b in range(a + 1 , (1000 - a)//2):
        c = 1000 - (a + b)
        if a**2 + b**2 == c**2:
            print(a * b * c , a , b , c)
            break
    else:
        continue
    break





さて、ここまでが今回の内容です
なかなか面白い問題ばかりですねえ


まだまだプロジェクトオイラーには
たくさんの問題が残っているので
これからも問題を解いていきたいと思います

それでは

今回は結構辛目な分数の問題を
プログラムで解いてみることにしました。


解説動画はこちら



さて、今回の問題は2022年の
市川高校という所の数学の問題のようです。 

スクリーンショット 2022-02-05 16.06.53

見てるだけでも嫌になりそうな問題ですね!!!

でもこれ、高校入試の問題だから
中学生が解いてるわけですよ

くーーー
私中学生以下ですねーー

悔しいのでプログラムの力を借りましょう。
Python言語では分数の計算を
手軽に行うことのできる
「fractions」というライブラリがあります。

これを使って分数の計算をしてしまいましょう。
ライブラリを読み込んで最初の部分の
計算をしてみましょう。
from fractions import Fraction

a = Fraction(1,6)
print(a)
1/6

Fraction(分子,分母)という形で
分数を表すことができます。

残りの部分も入力して行きましょう。
長いので変数に分割して行きます。
s1 = 4**4 + 4*  3**4
s2 = 4**4 + 4*11**4
s3 = 4**4 + 4*19**4
s4 = 4**4 + 4*27**4
s5 = 4**4 + 4*35**4

b1 = 4**4 + 4*  7**4
b2 = 4**4 + 4*15**4
b3 = 4**4 + 4*23**4
b4 = 4**4 + 4*31**4
b5 = 4**4 + 4*39**4

s = s1*s2*s3*s4*s5
b = b1*b2*b3*b4*b5

c = Fraction(s,b)
print(c)
1/337


最後の部分は
この2つを掛け合わせるだけですね!!
print(a * c)


はい、今年の入試問題にふさわしい
問題ではないでしょうか!!


っていうか、こんな数になる数式を
考え出した設問者がすごい!!!!!!

解く方はカンでもいけるかもしれないけど
こういう問題を作る方は凄いですね

今回は辛目な分数の問題を解いてみました。
Fraction、すっごく使えますよ
是非使ってみて下さい。

それでは。

今回はTwitterで出回っていた
電車の中吊り広告にあった
数学の問題をプログラムで解いて見ました。

解説動画はこちら



さて、問題は・・・

GAKKOUの6文字を並び替えてできる
360個の文字列を辞書式に並べるとき
100番目の文字列を求めよ


ということで
マジで難しい!!!

自分如きでは見当もつきません!!

こんな時はプログラムの力を借りて
解いてみることにしましょう。


Pythonでは文字列の組み合わせを作ってくれる
ライブラリがあるのでこれをimportしておきます。
import itertools

まず、データとして文字列を
リスト型で定義しておきます。
s = ['G','A','K','K','O','U']

次に文字列の組み合わせを作ります。
辞書形式ということですが
ここでは重複排除のできるSET型で
文字列の組み合わせを作ります。

itertools.permutations(データ,個数)
で順列を求めることができます。

第一引数は先ほど作った文字列のリスト
この中から6個の文字を取るので
第二引数は6になります。
res = {a for a in itertools.permutations(s,6)}

これで文字列の組み合わせが出来上がったので
最後に並び替えですね。

並び替えはsorted関数が使えます。
リスト形式にしてインデックス値の99番目が
100番目にくる文字列になるはずです。
''.join(list(sorted(res))[99])

答えは

・・
・・・


受験生にとって一番良い答えですね


Python言語はコードを書く量が少なくて済むのが
利点の一つでもあります。

このコードも1行で出来るようにしてみましょう。
こんな感じになりました。
import itertools;sorted({''.join(r) for r in itertools.permutations('GAKKOU',6)})[99]

はい

他の言語でもだいたいは1行でいけちゃいますね。
どの言語が書く文字数が少ないのか
調べて見たいものですね。

プログラムできる方は
是非考えて見て下さい。


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

今回は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つ
ご紹介させていただきました。

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

それでは。
 

このページのトップヘ