!!! サイト改修中のため表示が乱れる場合があります(1月末頃まで) !!!
数値解法 / 数値シミュレーション

16-6. モンテカルロ法でケーキ屋さんのコストを削減してみた

蓮コラ画像が含まれます。集合体恐怖症の方はご注意ください。

問題

次のような、断面にいちごが見える四角いケーキを作りたい。しかし、いちごのコストは削減したい。

よこ60cm、たて40cmのスポンジ生地に、直径3cmのいちごを散りばめる。生地を10×10個にカットしたとき、少なくとも1つのいちご断面を有するピースが90%以上となるいちごの最小個数を求めよ。

今回は数学をまったく使わずに、モンテカルロ法で答えを求めてみます。

数学ができない

大の大人が3人集まって理論で解こうとあれこれ考えてみましたが、「点と直線の距離」の公式が思い出せず断念しました。

通りかかった数学科出身の人に聞いてみたところ、

あーめんどくさい。(主に境界条件が)

と言われました。

参考文献

モンテカルロ法とは、ある値や確率が知りたいときに、コンピュータ内でサイコロを振りまくって実験的に求める方法です。

モンテカルロ法 - Wikipedia

実行環境

WinPython3.6をおすすめしています。

WinPython - Browse /WinPython_3.6/3.6.7.0 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows

Google Colaboratoryが利用可能です。

Google Colab

import

必要なパッケージをインポートします。

import numpy as np
import numpy.random as nr
from copy import deepcopy
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw

いちごの用意

いちごの画像を作ります。透明な四角の上に半透明な赤い円を置きます。R値は255、αチャンネルは100としました。

#いちご(透明な四角の上に半透明な赤い円を置いたもの)
ichigo = Image.new('RGBA', (31, 31), (0, 0, 0, 0))
draw_ichigo = ImageDraw.Draw(ichigo)
draw_ichigo.ellipse((0, 0, 30, 30), fill=(255, 0, 0, 100))

#表示
plt.imshow(ichigo)
plt.show()

ケーキの用意

ケーキの画像を作ります。ただの透明な四角です。

#ケーキ(透明な四角)
cake = Image.new('RGBA', (600, 400), (0, 0, 0, 0))

#表示
plt.imshow(cake)
plt.show()

ケーキにいちごを100個置いてみる

ケーキにいちごを置く」関数を作って、100回実行してみます。

#受け取ったケーキのランダムな位置にいちごを置いて返す関数
def put_ichigo(cake_tmp):
    x, y = nr.randint(0, 600-30), nr.randint(0, 400-30)
    cake_tmp.paste(ichigo, (x, y), ichigo) #透過画像として貼り付け
    return cake_tmp

#ケーキにいちごを100個置く
for i in range(100):
    cake = put_ichigo(cake)

#表示
plt.imshow(cake)
plt.show()

いちごが重ならないように置くには?

いちごをわざわざ透過画像にしたのには理由があります。いちご1個の部分はRGBのR成分が100ですが、いちごが2つ以上重なった部分はRが100より大きくなります。したがって、ケーキのR成分の最大値を調べることでいちごが重なっているかどうかを判断できます。

これを利用して、いちごが重ならないように100個置いてみます。

#受け取ったケーキの赤成分の最大値を返す関数
def get_redmax(cake_tmp):
    cake_np = np.array(cake_tmp)
    return np.max(cake_np[:,:,0])

#ケーキにいちごを100個置く。ただしイチゴが重なった場合は置き直す
ichigo_count = 0
while 1:
    #ケーキをコピーしていちごを置いてみる
    cake_copy = deepcopy(cake)
    cake_copy = put_ichigo(cake_copy)

    #いちごが重なっていなければ採用する
    if get_redmax(cake_copy) <= 100:
        cake = deepcopy(cake_copy)
        ichigo_count += 1
    
    #いちごが100個乗ったら終わり
    if ichigo_count == 100:
        break

#表示
plt.imshow(cake)
plt.show()

いちご断面があるかどうかの判断は?

ケーキを1ピースにカットしたとき、ピースの外周にR成分=100の部分があれば、そのピースにはいちご断面があるといえます。そこで、「ケーキをカットして1ピースを返す関数」「ピースの外周の赤成分の最大値を返す関数」を作ります。このケーキの全ピースを調べて、「いちご断面があるピース数」「全ピース数」を数えます。

#受け取ったケーキをカットして1ピースを返す関数
def cut_cake(cake_tmp, x, y):
    piece = cake_tmp.crop((x, y, x+60, y+40))
    return piece

#受け取ったピースの外周の赤成分の最大値を返す関数
def get_redmax_edge(cake_tmp):
    cake_np = np.array(cake_tmp)
    edge = np.concatenate([cake_np[0,:,0], cake_np[-1,:,0], cake_np[:,0,0], cake_np[:,-1,0]])
    return np.max(edge)

#ケーキを10×10にカットして「断面にいちごがあるピース数」と「全ピース数」を数える
ok_count = 0
all_count = 0
for x in range(0, 600, 60):
    for y in range(0, 400, 40):
        #カット
        piece = cut_cake(cake, x, y)
        
        #いちご断面があればOKカウントする
        if get_redmax_edge(piece) == 100:
            ok_count += 1
        
        #すべてのピースをカウントする
        all_count += 1

print('ok_count:{}'.format(ok_count))
print('all_count:{}'.format(all_count))
ok_count:98
all_count:100

100ピース中98ピースにいちご断面がありました。90%のピース(=90個)にいちご断面があればよいので、いちごを100個散りばめるのはちょっと無駄があるといえます。

サイコロを振りまくる

ここまでのコードを少し改造して(コードは省略)、いちごの数を0~100個、各1000回試行して平均値と標準偏差をプロットすると次のようになりました。

(略)
ichigo_num:72 mean:0.89919
ichigo_num:73 mean:0.90160
ichigo_num:74 mean:0.90704
ichigo_num:75 mean:0.91067
(略)

結論、いちごを73個散りばめれば90%以上のピースにいちご断面があらわれることが期待される

リアクションのお願い

「参考になった!」「刺激された!」と思ったらぜひリアクションをしましょう。エンジニアの世界はGive and Takeによって成り立っています。これからも無料で良質な情報にアクセスできるよう、Giveする人への感謝をリアクションで示しましょう!

この記事をシェアする

自身のブログ等で使用する場合は引用を忘れずに!

また、寄付も受け付けています。コーヒー1杯でとても喜びます(*˘︶˘*)

 Amazonでギフト券(アマギフ)を贈る

こちらのリンク から金額を指定してお贈りください。(デフォルトで10000円になっているのでご変更ください)

配送:Eメール
受取人:staffあっとvigne-cla.com
贈り主:あなたのお名前やニックネーム
メッセージ:◯◯の記事が参考になりました。など

のようにご入力ください。見返りはありませんのでご了承ください。

 Amazonで食事券(すかいらーく優待券)を贈る

500円 1000円 2000円 5000円 からお贈りください。

配送:Eメール
受取人:staffあっとvigne-cla.com
贈り主:あなたのお名前やニックネーム
メッセージ:◯◯の記事が参考になりました。など

のようにご入力ください。見返りはありませんのでご了承ください。

 その他、ギフト券やクーポン券をメールで贈る

デジタルのギフト券/クーポン券はメールアドレス(staffあっとvigne-cla.com)までお送りください。受領の返信をいたします。
紙のギフト券/クーポン券は 「郵便物はこちらへ」の住所 まで送付してください。名刺やメールアドレスを同封していただければ受領の連絡をいたします。
余った株主優待券等の処理におすすめです。
いずれも見返りはありませんのでご了承ください。

不明点はSNSでお気軽にご連絡ください

ビネクラのTwitter・Youtubeでコメントをください!


Slack・Discordの場合はこちらの公開グループに参加してShoya YasudaまでDMをください!


※当ブログに関することは何でもご相談・ご依頼可能です。

この記事を書いた人
Yasuda

博士(理学)。専門は免疫細胞、数理モデル、シミュレーション。米国、中国で研究に携わった。遺伝的アルゴリズム信者。物価上昇のため半額弁当とともに絶滅寸前。

タイトルとURLをコピーしました