乙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倍ほどは時間がかかるんじゃないでしょうかね

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

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

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

カイジファイナルゲームを観てきました。

そこで新作ゲームがあったので
プログラムで
検証していきたいと思います。

検証動画はこちら




さて
まずはゴールドジャンケンについてですね

この映画の内容では
政治家向けの接待ジャンケンと言う設定です。

ルールは
・3回のジャンケン勝負
・最低1回は必ずグーを出す必要がある(純金を握る)
・純金を手にして(グーの時)勝った場合、その純金はもらえる

3回勝負のうち
1回はグーを出すと言うのがポイントですね

映画では登場人物の
高倉という官僚が考えたというもので

カイジと対戦する機会があります。

その場合は
特別ルールが加算されていました。

カイジ戦特別ルール
・カイジが1回でも勝てば勝利。
・あいこの場合、高倉の勝ち。
・純金を握って勝つごとに1億円の金塊が貰える。

高倉は3回の勝ちを狙っているのでした。


と言うことで
早速プログラムで検証していきたいと思います。

まずジャンケンの手は
パー・グー・チョキ・
しか無いので
その重複ありの組み合わせになります。

ただし一度はグーを出さないといけないので
パーとチョキだけのケースを除くことを考えると
import itertools

# 手の内
seq = ['パー','グー','チョキ']

# 出せる手を求める

# itertools.product で直積を求める
# 1度でもグーを出す必要がある
hand = [h for h in itertools.product(seq,repeat=3) if 'グー' in h]

print(len(hand))

for h in hand:
    print(h)
19
('パー', 'パー', 'グー')
('パー', 'グー', 'パー')
('パー', 'グー', 'グー')
('パー', 'グー', 'チョキ')
('パー', 'チョキ', 'グー')
('グー', 'パー', 'パー')
('グー', 'パー', 'グー')
('グー', 'パー', 'チョキ')
('グー', 'グー', 'パー')
('グー', 'グー', 'グー')
('グー', 'グー', 'チョキ')
('グー', 'チョキ', 'パー')
('グー', 'チョキ', 'グー')
('グー', 'チョキ', 'チョキ')
('チョキ', 'パー', 'グー')
('チョキ', 'グー', 'パー')
('チョキ', 'グー', 'グー')
('チョキ', 'グー', 'チョキ')
('チョキ', 'チョキ', 'グー')

27通りのうち
19ケースの組み合わせが残ります。

次にこの19ケースのみで
総当たり戦を考えてみます。

高倉に対してカイジはあいこでも負けます。
カイジが勝てるのは勝つ組み合わせの時だけです。

19ケース同士の総当たりだと
361通りの対戦パターンがあります。
count=[]
for k,t in itertools.product(hand,hand):
    tmp={'kaiji':0,'takakura':0}
    for i in [0,1,2]:
        if (k[i]=='パー' and t[i]=='グー') or (k[i]=='グー' and t[i]=='チョキ') or (k[i]=='チョキ' and t[i]=='パー'):
                tmp['kaiji']+=1
        else:
                tmp['takakura']+=1
    count.append(tmp)
    print(k,t,tmp)

ここから勝敗結果を求めていくと
result = {}
for c in count:
    key = str(c)
    if key in result:
        result[key]+=1
    else:
        result[key]=1

for k,v in result.items():
    print(k,v)
{'kaiji': 0, 'takakura': 3} 115
{'kaiji': 1, 'takakura': 2} 159
{'kaiji': 2, 'takakura': 1} 75
{'kaiji': 3, 'takakura': 0} 12

カイジが1回でも勝てるケースは
361分の246ケースあります。

しかし、もう一つのポイントがあります。

2手目までにグーを出していない場合は
3手目でグーを出さないといけないので
相手に負けてしまうケースが存在します。
(相手も2手目までにグーを出していなければ引き分け)

ということは最後にグーを残すことは
無いだろうと思われるので
最後にグーを残してしまうケースを考えると

hand2 = []
for h in hand:
    if h[0]!='グー' and h[1]!='グー':
        print(h)
    else:
        hand2.append(h)

('パー', 'パー', 'グー')
('パー', 'チョキ', 'グー')
('チョキ', 'パー', 'グー')
('チョキ', 'チョキ', 'グー')

4ケースあります。

これを除いて考えてみることにします。

19-4で15ケースですね。
count=[]
for k,t in itertools.product(hand2,hand2):
    tmp={'kaiji':0,'takakura':0}
    for i in [0,1,2]:
        if (k[i]=='パー' and t[i]=='グー') or (k[i]=='グー' and t[i]=='チョキ') or (k[i]=='チョキ' and t[i]=='パー'):
                tmp['kaiji']+=1
        else:
                tmp['takakura']+=1
    count.append(tmp)
    print(k,t,tmp)

result = {}
for c in count:
    key = str(c)
    if key in result:
        result[key]+=1
    else:
        result[key]=1

for k,v in result.items():
    print(k,v)

{'kaiji': 0, 'takakura': 3} 78
{'kaiji': 1, 'takakura': 2} 99
{'kaiji': 2, 'takakura': 1} 42
{'kaiji': 3, 'takakura': 0} 6

結果としては

225ケースのうち
147回はカイジが1回でも勝てるようですね。

割合としては
65%くらいの確率で
1回は勝てることになるんじゃ
なかろうかと。

そもそも
このゲームのルールだと
カイジが有利なのかもしれませんね。

2回以上勝てたケースが
有利な手順かもしれないので
それを調べてみます。
w = {1:{'パー':0, 'グー':0,'チョキ':0},2:{'パー':0, 'グー':0,'チョキ':0},3:{'パー':0, 'グー':0,'チョキ':0}}
for k,t in itertools.product(hand2,hand2):
    tmp={'kaiji':0,'takakura':0}
    for i in [0,1,2]:
        if (k[i]=='パー' and t[i]=='グー') or (k[i]=='グー' and t[i]=='チョキ') or (k[i]=='チョキ' and t[i]=='パー'):
                tmp['kaiji']+=1
        else:
                tmp['takakura']+=1
    if "'kaiji': 2" in str(tmp) or "'kaiji': 3" in str(tmp):
        print(k,t,tmp)
        w[1][k[0]]+=1
        w[2][k[1]]+=1
        w[3][k[2]]+=1
print(w)
{1: {'パー': 15, 'グー': 27, 'チョキ': 6},
 2: {'パー': 15, 'グー': 27, 'チョキ': 6},
 3: {'パー': 16, 'グー': 16, 'チョキ': 16}}


手の結果から考慮すると

1手目は「パー」か「グー」
2手目は1手目「パー」なら「グー」, 1手目「グー」なら「パー」
3手目は「パー」か「チョキ」

というパターンで出すと
勝ちやすいのかもしれない・・・


てか
そもそも
ジャンケンに必勝法なんて
存在しないですよねーーーーーーー

カイジが
勝てたのかどうかは
カイジファイナルゲームをご覧ください


それでは

新年あけましておめでとうございます。

今年が良い年になりますように
と言うことで

昨年末に大きなニュースがありました。
あのお方と似ている人の顔を入れ替えて
遊んでみました。


解説動画はこちら



まあ詳しくは動画の方をご覧くださいませ。

出来上がったものの中から
いくつかを置いておきますねwww
gonebean1

gonebean2

カルロス・ビーンさんと
Mr ゴーンさんですね。

ってか
Mr ゴーンだと
変わってねーーーー


最後にこちらは誰と
入れ替えたでしょうか?!?!
goneyoshi


と言うことで
今年も色々
プログラミングを用いて
面白おかしい講座をやっていきたいと思います。

今年度もよろしくお願いいたします。
それでは
 

ワードクラウドづくりにハマっております。

ワードクラウドは文章中で

出現頻度が高い単語を複数選び出し
その頻度に応じた大きさで図表化する手法のことで
文章の特徴を一目で見ることができると言うものです。


解説動画はこちら



Pythonでは
wordcloud
と言うライブラリがあるので
これを用いるとワードクラウドを簡単に作成できます。

インストール方法は

pip install wordcloud

これでライブラリがインストールされたら
必要なものとしては
文章をまとめたものが必要です。

スクレイピングや
形態素解析などして
集めて集計すればできます。

名詞や動詞など意味のあるものだけを残し
要らない助詞などは削ります。

単語と単語の間はスペースなどで
空けておきましょう。

ファイル化したら
ワードクラウドの作成です。

嵐の歌詞を集めて
ワードクラウドを作ってみましょう。

ワードクラウドは次のようなコードで作成できます。
from wordcloud import WordCloud
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

with open('arashi_list.txt', encoding='utf-8') as _f:
    text = _f.read()
font_path = '/System/Library/Fonts/ヒラギノ丸ゴ ProN W4.ttc'
exc_words = ['たち','みる','そう', 'ない', 'いる', 'する', 'まま', 'よう', 'てる', 'なる', 'こと', 'もう', 'いい', 'ある', 'ゆく', 'れる']
wordcloud = WordCloud(background_color='white',
    font_path=font_path, width=800, height=600, stopwords=set(exc_words)).generate(text)
wordcloud.to_file('arashi.png')

im = Image.open('arashi.png')
plt.figure(figsize=(16,12))
plt.imshow(im)
plt.tick_params(labelbottom=False,labelleft=False,labelright=False,labeltop=False)
plt.tick_params(bottom=False,left=False,right=False,top=False)
plt.show()

ワードクラウドを作るには
フォントの指定が必要です。

日本語でやるには日本語対応のフォントが必要です。
上記の例はmacなので
/System/Library/Fonts/
フォルダを指定していますが
Windowsの人は

C:/Windows/Fonts/

配下になると思います。
.ttc拡張子のファイルを指定しましょう。

フォントをうまく指定できたら
画像ファイルができるので
それを表示してみると

download-2

はい出来ました。

僕らを筆頭に未来、明日、きっとなど
すごく明るい未来が感じられる世界観の歌詞だと
見受けられますねーーー

1曲も知らないけどwww

こんな感じですぐに作ることができるので
めちゃくちゃ楽しいです。

今度は
米津玄師さんの歌詞でやってみましょう!!!

こうなりました。
download-1

あなた、笑う が
中心の世界観です。

嵐と比べてみると
download

人称の違いや
言葉のチョイスが対照的で面白いですね

・嵐は自分中心
・米津さんは相手想い

そんな歌詞なんだろうと思いますね。
Lemonしか知らないけどwww

非常に面白いし、理解しやすいので
表現方法として優秀です。

皆さんも
作ってみてはいかがでしょうか?

それでは

どうやら
大河ドラマの代役がキマったようですね!!!

キマった言うても
代役の方ですよーーー

代役は
川口春奈さん

と言うことで
沢尻エリカさんと川口春奈さんの写真を用いて
濃姫役を川口春奈さんがやった場合の
イメージがどうなるかを検証してみましょう!!


解説動画はこちら





まず画像の加工などは
opencvを用いて行なっています。

opencvを用いて
画像を油絵ぽくすることが出来ます。

ソースはこれ

import cv2
import numpy as np

img1 = cv2.imread('ファイルパス')

oil_filter = np.array([[-1,-1,-1,-1,-1,-1,-1],[-1, 0, 0, 0, 0, 0,-1],[-1, 0, 0, 0, 0, 0,-1],[-1, 0, 0,26, 0, 0,-1],
                       [-1, 0, 0, 0, 0, 0,-1],[-1, 0, 0, 0, 0, 0,-1],[-1,-1,-1,-1,-1,-1,-1]], np.float32) / 2.0

oilpaint = cv2.filter2D(cv2.medianBlur(img, 39), -13, oil_filter)

plt.figure(figsize=(16,10))
plt.imshow(cv2.cvtColor(oilpaint, cv2.COLOR_BGR2RGB))
plt.tick_params(labelbottom=False,labelleft=False,labelright=False,labeltop=False)
plt.tick_params(bottom=False,left=False,right=False,top=False)
plt.show()

もし画像をお持ちでしたら
試しにやってみてください。

写真を油絵っぽくするには
まずブラーをかけています。

ブラーは「ぼかし」ですね。

次にフィルターをかけています。
フィルターは行列の数値の計算をしている感じです。
numpyを用いていますので
数値を変えることで色々な加工が出来ます。

あとは
顔を入れ替えるのは
前回の動画でもやっている
フェイススワッピングアプリで
行なっています。

出来上がりの結果は・・・
download

こわっっ!!!!!

どうやら
春菜違いで
合成に使う写真を間違っちゃったようですねーーー

近藤春菜さんを
合成してしまいました!!

とまあ
かなり無理やりですが
合成できてしまうのが
プログラムの悪い所

右京さんもびっくりです。

川口春奈さんを
濃姫に合成した結果は
動画の方をご覧くださいませ。


それでは

このページのトップヘ