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

プログラミング

先日投稿されたクソデカ羅生門

これを見ていたら羅生門との
違いが知りたくなりました。


解説動画はこちら



さて
元ネタはこちらです。

クソデカ羅生門 羅生門
 
さて違いを見たいならば差分を取る!!!
という事でDIFFツールとかを使えば
多分超簡単だと思います。

それだとつまんないので
差分を取るプログラムを考えたいです。

差分を取る手順は以下の通りです。
1.両方の奴をファイル化する
2.不要文字(スペース)は削除
3.1行ずつ差分をとる
4.差分の所を囲む

まずは先ほどのリンクから
2つをファイル化します。

ファイル化が終わったら取り込みします。
# ファイルの読み込み
with open('羅生門/クソでか羅生門.txt') as _f:
    text = _f.read()
text = text.replace('。','。\n')
kusodeka = text.split('\n')
print(len(kusodeka))

with open('羅生門/羅生門.txt') as _f:
    text = _f.read()
text = text.replace('。','。\n')
rasyoumon = text.split('\n')
print(len(rasyoumon))


続いて差分をチェックします。
こんな関数でチェックして
元の文と違いに分けました。
# 差分チェック
def check_diff(moto,kuso):
    res1,res2,tmp = [],[],kuso
    for m in moto:
        i = str(tmp[0:]).find(m)
        diff = tmp[0:i]
        if len(diff)>0:
            for d in diff:
                res2.append(d)
                res1.append(' ')
        res2.append(' ')
        res1.append(tmp[i])
        tmp = tmp[i+1:]
    return res1,res2
最後に出力ですがこんな感じで
違い部分を囲んで表示させることにしました。
# 差分の囲み
def  make_text(r1,r2):
    text,flg = '',1
    for i in range(len(r1)):
        if r1[i]==' ':
            if flg==1:
                text+='`'
                flg*=-1
            text+=r2[i]
        else:
            if flg==-1:
                text+='`'
                flg*=-1
            text+=r1[i]
    return text

# 差分の文章化
for moto,kuso in zip(rasyoumon,kusodeka):
    res1,res2 = check_diff(moto,kuso)
    text = make_text(res1,res2)
    print(text)

ある日の`超`暮方`(ほぼ夜)`の事である。
一人の下人が、`クソデカい`羅生門の
`完全な真`下で雨やみを
`気持ち悪いほどずっと`待`ちまく`っていた。

`馬鹿みたいに`広い門の`真`下には、
この`大`男のほかに`全然`誰もいない。

ただ、所々丹塗の`びっくりするくらい`剥げた、
`信じられないほど`大きな円柱に、
`象くらいある`蟋蟀が一匹とまっている。・・・

こんな感じになりました。
``で囲んだ部分が違いのところですね。

どうやら、元の羅生門に対して
クソデカくする修飾語を
たくさんつけた感じになってます。

さて、どんな言葉が使われているんでしょうか?

calc = {}
for moto,kuso in zip(rasyoumon,kusodeka):
    res1,res2 = check_diff(moto,kuso)
    
    tmp = ''
    for r in res2:
        if r==' ':
            if tmp in calc:
                calc[tmp]+=1
            else:
                calc[tmp]  =1
            tmp=''
        else:
            tmp+=r

calc.pop('')
print('種類数 : ',len(calc))
for k,v in sorted(calc.items(),reverse=True,key=lambda x:x[1]):
    print(v,'\t',k)
種類数 :  341
26 	 大
13 	 まくっ
12 	 超
10 	 真
10 	 メチャメチャ
10 	  Godsに影響した
9 	 巨大
8 	 糞
7 	 クソデカ
7 	 倒し
7 	 完全に
7 	 巨大な
6 	 超巨大
6 	 巨
5 	 クソ
5 	 マジで
4 	 クソデカい
4 	 全然
4 	 マジで全然
4 	 豪
4 	 超苦しい
4 	 死ぬほど
4 	 大量に
3 	 千
3 	 世界最強の
3 	 に影響した
3 	 バカ
3 	 極
3 	 最強
3 	 王
3 	 まくり
3 	 本当に
3 	 まったく
3 	 めちゃくちゃ
2 	 (ほぼ夜)
2 	 百
2 	 毎日
2 	 りまく
2 	 完全
2 	 きったない
2 	 ハチャメチャに
2 	 びっくりするほど
2 	 炎
2 	 最高級
2 	 〇〇
2 	 続け
2 	 剛
2 	 〇〇〇〇
2 	 絶対に
2 	 雑魚
2 	 瞬間的に
2 	 極悪
2 	 まぶた
2 	 さかっ
2 	 いまく
2 	 巨大怪
1 	 完全な真
1 	 気持ち悪いほどずっと
1 	 ちまく
1 	 馬鹿みたいに
1 	 びっくりするくらい
1 	 信じられないほど
1 	 象くらいある
1 	 大河のように広い
1 	 狂った
1 	 激・
1 	 爆裂
1 	 珍妙
1 	 マジで全く
1 	 破壊的
1 	 最強大
1 	 極限
1 	 エグすぎる
1 	 こ
1 	 クソ広い
1 	 マジでもう
1 	 とかそういうレベル
1 	 文化財クラスの
1 	 ものすごいパワーで
1 	 ベッチャベチャに
1 	 もうイヤになっちゃうくらい
1 	 親の仇のようにメチャメチャ
1 	 クソ治安がいいことで知られる
1 	 正気を疑うレベルでデカい
1 	 ドン引きするくらい
1 	 クソヤバい

はい、超大量の語句の数々
その数にして341語句

こんだけ考えるのにどんだけな
時間が掛かったでしょうか。

考えるだけでもすごいですね。

文字数で見るとどんだけ長いものがあるでしょうか?

ソートのキーを文字数にして数えてみます。
for k,v in sorted(calc.items(),reverse=True,key=lambda x:len(x[0])):
    print(len(k),'\t',k)
23 	 Ultimet-Sentimentalisme
20 	 ただでさえ最低最悪のゴミの掃き溜めである
17 	 ほとんど聞き取れないほどの超早口で
16 	 マジで悲しくなっちゃうくらい全然
16 	 of the Godsに影響した
15 	 ブッサイクで気持ちの悪い巨大な
14 	 クソ治安がいいことで知られる
14 	 三千里(約一万二千メートル)
13 	 もうイヤになっちゃうくらい
13 	 親の仇のようにメチャメチャ
13 	 のちに剣聖と呼ばれる最強の
13 	 地の果てまで広がるがごとき
13 	 自殺したくなるくらい本当に
13 	 強烈な殺意を内包した本気の
12 	 正気を疑うレベルでデカい
12 	 マジでビックリするくらい
12 	 本当に惨めな感じになって
12 	 寸分たりとも違わず完全に
12 	 物理的にありえない動きで
12 	 本当にめちゃめちゃ苦しい
12 	 世界最高の名刀と謳われる
12 	 テレパシーのごとく完全に
11 	 怖いくらい全然まったく
11 	 鼓膜破壊レベルの音量で
11 	 頭おかしいくらいデカい
11 	 トチ狂ったクソデカさの
11 	 意味わからんくらいクソ
11 	 信じられないほどデカい
11 	 構造的にありえない形で
10 	 気持ち悪いほどずっと
10 	 思わず目を疑うくらい
10 	  Godsに影響した
10 	 本当にマジでまったく
10 	 メチャメチャくっせえ
10 	 芸術品のように美しい
10 	 超メチャメチャ剣呑な
10 	 頬が落ちるほど本当に
10 	 まったく一瞬たりとも
10 	 目にも止まらないほど
10 	 えげつないスピードで

10文字以上の長い語句も これだけあります。

こりゃあ、元のボリュームに比べて
デカくなるわけですね。

これだけの文字数に語句の種類
恐るべき語彙力です。

元々の羅生門に圧倒的なクソでか表現が加わる
これがこの作品の魅力であると思いますね。

皆様も
興味があれば違いをみながら
読んでみてください。

それでは。

画像用のフィルターで、面白いものを見つけました。

解説動画はこちら



さてフィルターの詳細はこちらです。

KuwaharaFilter

参考:Wikipedia 

桑原フィルターは桑原さんが考案した
平滑化フィルターの一種で
まるで油絵のようになるのが特徴です。

実装している方がいたので
参考にして見ました。

ソースはこちら
import matplotlib.pyplot as plt
import numpy as np
import cv2
%matplotlib inline

def kuwahara_filter(img,r=3):
    h,w,_ = img.shape
    img = np.pad(img,((r,r),(r,r),(0,0)),"edge")
    a,v = cv2.integral2(img)
    
    a=(a[:-r-1,:-r-1]+a[r+1:,r+1:]-a[r+1:,:-r-1]-a[:-r-1,r+1:])/(r+1)**2
    v=((v[:-r-1,:-r-1]+v[r+1:,r+1:]-v[r+1:,:-r-1]-v[:-r-1,r+1:])/(r+1)**2-a**2).sum(axis=2)
    def result(i,j):
        return np.array([a[i,j],a[i+r,j],a[i,j+r],a[i+r,j+r]])[
            (np.array([v[i,j],v[i+r,j],v[i,j+r],v[i+r,j+r]]).argmin(axis=0).flatten(),
            j.flatten(),i.flatten())].reshape(w,h,_).transpose(1,0,2)
    return result(*np.meshgrid(np.arange(h),np.arange(w))).astype(img.dtype)

イメージと大きさだけ指定してもらえば
フィルターがかかった画像が返ってきます。

# 画像の読み込み
img = cv2.imread('画像のパス', cv2.IMREAD_COLOR)

# フィルターをかける
img2 = kuwahara_filter(img,サイズを数値で指定)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)

# 描画
plt.figure(figsize=(10,10))
plt.imshow(img2)
plt.show()

画像のパスとフィルターのサイズだけ
変えてもらえば動くと思います。

どんな結果になるのかは
見てもらったほうが早いですよね。

写真にかけると油絵風になります。
download
download-1

とっても幻想的ですねー

図工の授業とか、真似してかけば
良い点が取れそうな感じです。

写真の加工にはもってこいの
フィルターだと思うので
気になる方は試して見てください。

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

浮気や不倫がワイドショーで賑わっておりますが 浮気する人の顔に特徴はあるんでしょうか? 過去に不倫などの報道があった方で平均顔を作って検証して見ました。

検証動画はこちら


やっておることは
顔の目の位置に合わせて
顔の画像を重ねているだけ

ソースは長いので割愛

気になる方は
いろんな方で
試して見てくださいね!!

それでは。

国民的長寿番組であるサザエさん

先週あることが話題になりました。


解説動画はこちら



さて話題になったのは
サザエさんのじゃんけんに関することなのですが

何が起きたかは分からないですよね。
まずはサザエさんのじゃんけんを見てみることにしましょう。

ありがたいことに
サザエさんのじゃんけんの結果を
まとめていただいているサイトがありました。

サザエさんじゃんけん研究所
公式ウェブサイト


こちらのデータを参考にさせて頂くこととします。

こちらをファイル化して進めますが
やりたい方は
ご自身で用意してください。

さてファイルを読み込みます。
data = []
with open('sazae.csv') as _f:
    for row in _f:
        rows = row.replace('\n','').split('\t')
        data.append(rows)
print(len(data))
print(data[0],data[-1])
1439
['第1回', '1991年11月10日', 'チョキ'] ['第1439回', '2020年6月7日', 'パー']

こんな感じのデータです。

初回は1991年
最新回は2020年6月7日の結果です。

さて1439回分のデータあるので
集計をしてみましょう。

手の内訳は
result = {}
for d in data:
    if d[2] in result:
        result[d[2]]+=1
    else:
        result[d[2]]=1
print(result)
print(['{0} : {1}'.format(k,v/len(data)) for k,v in result.items()])
{'チョキ': 500, 'グー': 463, 'パー': 476}
['チョキ : 0.34746351633078526', 'グー : 0.32175121612230717', 'パー : 0.33078526754690757']

パー、グー、チョキの回数は
割と均等ですね。

少しだけチョキが多いようです。

お次は複数回での手が
どうなるかを見てみます。

次のような関数で
複数回の手を集計できます。
# 複数回の手の結果を計算する関数
def check_result(num,data):
    result = {}
    for i in range(len(data)-(num-1)):
        key = ''.join([data[i+j][2] for j in range(num)])
        if key in result:
            result[key]+=1
        else:
            result[key]=1
    return result
2回での手の内訳
result2 = check_result(2,data)
for k,v in sorted(result2.items()):
    print(k,v)
グーグー 97
グーチョキ 196
グーパー 170
チョキグー 199
チョキチョキ 94
チョキパー 207
パーグー 167
パーチョキ 209
パーパー 99

連続になると少し回数が減りますねーー

3回での手の内訳
result3 = check_result(3,data)
for k,v in sorted(result3.items()):
    print(k,v)
グーグーグー 7
グーグーチョキ 50
グーグーパー 40
グーチョキグー 46
グーチョキチョキ 45
グーチョキパー 105
グーパーグー 31
グーパーチョキ 92
グーパーパー 47
チョキグーグー 40
チョキグーチョキ 59
チョキグーパー 100
チョキチョキグー 42
チョキチョキチョキ 4
チョキチョキパー 48
チョキパーグー 101
チョキパーチョキ 60
チョキパーパー 46
パーグーグー 50
パーグーチョキ 87
パーグーパー 30
パーチョキグー 110
パーチョキチョキ 45
パーチョキパー 54
パーパーグー 35
パーパーチョキ 57
パーパーパー 6

さて、複数回の組み合わせとしては
こんな感じの結果になりました。

それでは連続でN回同じ手を出すは
どれくらいあるでしょうか?

次のような関数で調査しました。
# 連続手の結果を計算する関数
def continuous_result(num,data):
    result = {}
    targets = ['グー'*num , 'パー'*num , 'チョキ'*num]
    for i in range(len(data)-(num-1)):
        key = ''.join([data[i+j][2] for j in range(num)])
        if key in targets:
            if key in result:
                result[key]+=1
            else:
                result[key]=1
    return result

全じゃんけんの回数で割って確率をみてみます。

2連続
cresult2 = continuous_result(2,data)
for k,v in sorted(cresult2.items()):
    print('{0:10}'.format(k),'\t',v,'\t','{0:.5%}'.format(v/len(data)))
グーグー       	 97 	 6.74079%
チョキチョキ     	 94 	 6.53231%
パーパー       	 99 	 6.87978%

3連続
グーグーグー     	 7 	 0.48645%
チョキチョキチョキ  	 4 	 0.27797%
パーパーパー     	 6 	 0.41696%

4連続
グーグーグーグー   	 2 	 0.13899%
パーパーパーパー   	 2 	 0.13899%

5連続
パーパーパーパーパー 	 1 	 0.06949%

さて、連続で同じ手を出すのは
極端に少ないようです。

今までは4連続が最高だったようですが
この度、5連続でパーを初めて出したので
ニュースになったようです。

確率で言うと0.06%
ニュースになるのも頷けるかもしれません。

5回じゃんけんした際の手の組み合わせは
3通りの手があるので
3**5 = 243通りの組み合わせがあります。

その確率だとだいたい0.4%なので
それに比べると少ないですね。

じゃんけんの結果をみると
やや連続での組み合わせの回数が
少ないように感じます。

グーグーグー    	      7 	 0.48645%
グーグーチョキ   	     50 	 3.47464%
グーグーパー    	     40 	 2.77971%
グーチョキグー   	     46 	 3.19666%
グーチョキチョキ  	     45 	 3.12717%
グーチョキパー   	    105 	 7.29673%
グーパーグー    	     31 	 2.15427%
グーパーチョキ   	     92 	 6.39333%
グーパーパー    	     47 	 3.26616%
チョキグーグー   	     40 	 2.77971%
チョキグーチョキ  	     59 	 4.10007%
チョキグーパー   	    100 	 6.94927%
チョキチョキグー  	     42 	 2.91869%
チョキチョキチョキ 	      4 	 0.27797%
チョキチョキパー  	     48 	 3.33565%
チョキパーグー   	    101 	 7.01876%
チョキパーチョキ  	     60 	 4.16956%
チョキパーパー   	     46 	 3.19666%
パーグーグー    	     50 	 3.47464%
パーグーチョキ   	     87 	 6.04587%
パーグーパー    	     30 	 2.08478%
パーチョキグー   	    110 	 7.64420%
パーチョキチョキ  	     45 	 3.12717%
パーチョキパー   	     54 	 3.75261%
パーパーグー    	     35 	 2.43224%
パーパーチョキ   	     57 	 3.96108%
パーパーパー    	      6 	 0.41696%

例えば3回じゃんけんした際の組み合わせは
3**3で27通りなので
1パターンあたりは3.7%ほど

連続で同じ手を出すのは大分
少ないようですね。

と言うことは
仮説:前回と同じ手、連続手を
極端に嫌っているのではないか?

と言うことで

検証:前回と違う手をランダムにさしたら
勝率はどうなるのか?

100回対戦して平均を求めて見たいと
思います。

import numpy as np
hand = ['グー' , 'パー' , 'チョキ']
all_result = []
for j in range(100):
    result = {'勝ち':0,'負け':0,'あいこ':0}
    for i in range(len(data)):
        sazae = data[i][2]
        if i==0:
            you = np.random.choice(hand)
        else:
            you = np.random.choice(list(set(hand)-set([last])))
        if you==sazae:
            result['あいこ']+=1
            a='あいこ'
        else:
            if any([you=='グー' and sazae=='チョキ',you=='パー' and sazae=='グー',you=='チョキ' and sazae=='パー']):
                result['勝ち']+=1
                a='勝ち'
            else:
                result['負け']+=1
                a = '負け'
        last = sazae
    print(result)
    all_result.append(result)
win = sum([r['勝ち'] for r in all_result])//100
lose = sum([r['負け'] for r in all_result])//100
aiko = sum([r['あいこ'] for r in all_result])//100
print(win,lose,aiko)
436 430 571

思ったより勝ててないですねwww

まあ前回のと違うのをランダムで出したら
勝ちかあいこかに振られるので
あいこが多くなっちゃってます。

前回
前々回
前々々回

みたいに、過去の傾向を学習してあげないと
勝率は上がらないでしょうねーーー

なんか前々々世みたいにだな!!

と言うことで、サザエさんの
じゃんけんでした。

勝ちたい方はデータを見て
過去の傾向やパターンを見つけたら
勝率が上がるかもしれません。

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

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

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

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

解説動画はこちら




さてパスカルの三角形は
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

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

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

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

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

このページのトップヘ