[PR]当サイトはアフィリエイト広告を利用しています。
当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
参考にしたのは以下。
松井泰子 根本俊男 宇野毅明(2008)『入門オペレーションズ・リサーチ』東海大学出版部.
小田倉 友紀“レジに準備すべき釣り銭の提案”文教大学 卒業論文.
イベントで屋台を出す場合、釣銭をいくら準備するべきかというのはなかなか面倒な問題です。
この問題に対する計算方法としてモンテカルロシミュレーションがあります。
今回は屋台で出す品物は一つの、比較的簡単な部類の問題を扱います。
※今回の例題はリアルさを意識した結果、少し難しい内容となっております。
釣銭問題の入門記事を書きましたのでよろしければそちらもご覧ください。
問題は以下。
文化祭の屋台で焼きそばを売ることにした。
焼きそばは一食300円である。
昨年一日に売れたのは200食であった。
釣銭はどのくらい用意すればよいだろうか。
条件:
支払いは10000円札、5000円札、1000円札、500円玉、100円玉に限定する。
釣銭として用意する紙幣、貨幣は5000円札、1000円札、500円玉、100円玉とする。
お客を待たせる行列を避けたい、作っているうちに冷めるなどの理由で一度に注文できるのは最大5食とする。
モンテカルロシミュレーションで重要なのは、お客がどの現金を支払うかという点です。
10000円を200人が支払うということはないでしょう。
しかし一度に一人のお客が5食、1500円の支払いとなると10000円を支払う可能性はあります。
考えなければいけないのは、一人のお客が何食頼むのかという確率分布と、それぞれの販売個数におけるお客が支払う現金の確率分布です。
以下で場合分けして確率を予想してみます。ここの匙加減で結果はかなりぶれますが、とりあえず決めてみないと始まりません。
本当は販売一回ごとに紙幣と硬貨のやり取りをすべて記録して5年分くらいのデータから求めたいところです。ですが実際は過去のデータを入手できる可能性は低く、なんとなく決めてみるしかないと思います。
①お客が1食(支払額300円)注文するとき
1万円出す確率:0.07
5千円出す確率:0.03
千円出す確率:0.45
500円硬貨を出す確率:0.15
百円硬貨を3枚出す確率:0.3
②お客が2食(支払額600円)注文するとき
1万円出す確率:0.07
5千円出す確率:0.03
千円出す確率:0.60
500円硬貨と百円硬貨を出す確率:0.15
百円硬貨を6枚出す確率:0.15
③お客が3食(支払額900円)注文するとき
1万円出す確率:0.15
5千円出す確率:0.1
千円出す確率:0.6
500円硬貨と百円硬貨を出す確率:0.1
百円硬貨を9枚出す確率:0.05
④お客が4食(支払額1200円)注文するとき
1万円出す確率:0.15
1万円と200円出す確率:0.15
5千円出す確率:0.1
5千円と200円出す確率:0.05
千円と200円出す確率:0.3
千円と500円硬貨を出す確率:0.1
500円硬貨2枚と百円硬貨を出す確率:0.1
500円硬貨1枚と百円硬貨7枚出す確率:0.03
百円硬貨を12枚出す確率:0.02
⑤お客が5食(支払額1500円)注文するとき
1万円出す確率:0.1
1万円と500円硬貨を出す確率:0.05
1万円と100円硬貨を出す確率:0.05
5千円出す確率:0.1
5千円と500円硬貨を出す確率:0.05
5千円と100円硬貨を出す確率:0.05
千円と500円硬貨を出す確率:0.3
千円と100円硬貨を出す確率:0.2
500円硬貨3枚出す確率:0.05
500円硬貨2枚と百円硬貨5枚出す確率:0.04
百円硬貨を15枚出す確率:0.01
そして一度の販売個数の確率分布は以下とする。
一食の確率:0.25
二食の確率:0.4
三食の確率:0.2
四食の確率:0.1
五食の確率:0.05
アルゴリズム:
①0から1までの乱数を発生させ、販売個数の確率分布と照合。例えば乱数が0.68だった場合、累積確率0.65から0.85の三食に分類される。
②0から1までの乱数を発生させ、支払いパターンと照合。例えば乱数が0.87だった場合、三食の累積確率0.85から0.95の千円札一枚を出す場合に分類される。
③支払いパターンで各紙幣・硬貨の量を増減。例えば上の場合は千円札をプラス1、百円硬貨をマイナス1となる。
④:①から③までを販売した個数の合計が200以上になるまで繰り返す。
最終的に各紙幣・硬貨の履歴から最も負の値の絶対値が大きい値がその日最も不足した枚数なのでそれを記録しておく。
①から④を1000回程度繰り返せば、どの程度釣銭用の現金を用意すればよいか予想できるはずである。
アルゴリズムに基づいたプログラム(Python):
import random
import copy
class Turisen:
def __init__(self): # 初期化: インスタンス作成時に自動的に呼ばれる
self.cash = [0,0,0,0,0]
self.cashMin = [0,0,0,0,0]
oneSell = [0.07,0.1,0.55,0.7,1.0]
oneSellExchange = [[1,-1,-4,-1,-2],[0,1,-4,-1,-2],[0,0,1,-1,-2],[0,0,0,1,-2],[0,0,0,0,3]]
twoSell = [0.07,0.1,0.7,0.85,1.0]
twoSellExchange = [[1,-1,-4,0,-4],[0,1,-4,0,-4],[0,0,1,0,-4],[0,0,0,1,1],[0,0,0,0,6]]
threeSell = [0.15,0.25,0.85,0.95,1.0]
threeSellExchange = [[1,-1,-4,0,-1],[0,1,-4,0,-1],[0,0,1,0,-1],[0,0,0,1,4],[0,0,0,0,9]]
fourSell = [0.15,0.3,0.4,0.45,0.75,0.85,0.95,0.98,1.0]
fourSellExchange = [[1,-1,-3,-1,-3],[1,-1,-4,0,2],[0,1,-3,-1,-3],[0,1,-4,0,2],[0,0,1,0,2],[0,0,1,1,-3],[0,0,0,2,2],[0,0,0,1,7],[0,0,0,0,12]]
fiveSell = [0.1,0.15,0.2,0.3,0.35,0.4,0.7,0.9,0.95,0.99,1.0]
fiveSellExchange = [[1,-1,-3,-1,0],[1,-1,-4,1,0],[1,-1,-4,0,5],[0,1,-3,-1,0],[0,1,-4,1,0],[0,1,-4,0,5],[0,0,1,1,0],[0,0,1,0,5],[0,0,0,3,0],[0,0,0,2,5],[0,0,0,0,15]]
SellNumber = [0.25,0.65,0.85,0.95,1.0]
def classify(self):
p1 = random.random()
p2 = random.random()
cashNum = 0
exNum = 0
exList = []
for i in range(len(self.SellNumber)):
if(p1 < self.SellNumber[i]):
cashNum = i
break
if(cashNum == 0):
for i in range(len(self.oneSell)):
if(p2 < self.oneSell[i]):
exNum = i
exList = self.oneSellExchange[i]
break
if(cashNum == 1):
for i in range(len(self.twoSell)):
if(p2 < self.twoSell[i]):
exNum = i
exList = self.twoSellExchange[i]
break
if(cashNum == 2):
for i in range(len(self.threeSell)):
if(p2 < self.threeSell[i]):
exNum = i
exList = self.threeSellExchange[i]
break
if(cashNum == 3):
for i in range(len(self.fourSell)):
if(p2 < self.fourSell[i]):
exNum = i
exList = self.fourSellExchange[i]
break
if(cashNum == 4):
for i in range(len(self.fiveSell)):
if(p2 < self.fiveSell[i]):
exNum = i
exList = self.fiveSellExchange[i]
break
return exList, cashNum, exNum
def sumList(self,x):
for i in range(5):
self.cash[i] += x[i]
if(self.cash[i] < self.cashMin[i]):
self.cashMin[i] = self.cash[i]
minCashList = []
instList = []
for i in range(1000):
allSell = 0
instList.append(Turisen())
for j in range(200):
ex, cash, exNum = instList[i].classify()
instList[i].sumList(ex)
allSell += (cash + 1)
if(allSell > 200):
minCashList.append(copy.deepcopy(instList[i].cashMin))
break
moneyList = [[],[],[],[],[]]
for i in range(1000):
for j in range(5):
moneyList[j].append(minCashList[i][j])
for i in range(5):
print(i,"番目の現金の最小値")
print(min(moneyList[i]))
freqTable1 = [0,0,0,0,0,0,0,0,0]
freqTable2 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
freqTable3 = [0,0,0,0,0,0,0,0,0]
freqTable4 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
for i in range(1000):
for j in range(len(freqTable1)):
if(moneyList[1][i] > -(j + 1) * 3):
freqTable1[j] += 1
break
for j in range(len(freqTable2)):
if(moneyList[2][i] > -(j + 1) * 5):
freqTable2[j] += 1
break
for j in range(len(freqTable3)):
if(moneyList[3][i] > -(j + 1) * 3):
freqTable3[j] += 1
break
for j in range(len(freqTable4)):
if(moneyList[4][i] > -(j + 1) * 10):
freqTable4[j] += 1
break
print("1番目の度数分布")
print(freqTable1)
print("2番目の度数分布")
print(freqTable2)
print("3番目の度数分布")
print(freqTable3)
print("4番目の度数分布")
print(freqTable4)
出力:
0 番目の現金の最小値
01 番目の現金の最小値
-16
2 番目の現金の最小値
-72
3 番目の現金の最小値
-17
4 番目の現金の最小値
-165
1番目の度数分布
[173, 327, 312, 146, 34, 8, 0, 0, 0]
2番目の度数分布
[115, 126, 143, 125, 150, 111, 79, 53, 50, 23, 14, 3, 5, 1, 2, 0, 0, 0, 0, 0]
3番目の度数分布
[433, 296, 174, 63, 22, 12, 0, 0, 0]
4番目の度数分布
[26, 25, 53, 80, 111, 156, 139, 119, 105, 73, 60, 25, 10, 11, 3, 2, 2, 0, 0, 0]
これを基に度数分布表を作成します。
5000円札:
階級 | 度数 | 相対度数 | 累積相対度数 |
0~3 | 173 | 0.173 | 0.173 |
6 | 327 | 0.327 | 0.5 |
9 | 312 | 0.312 | 0.812 |
12 | 146 | 0.146 | 0.958 |
15 | 34 | 0.034 | 0.992 |
18 | 8 | 0.008 | 1 |
21 | 0 | 0 | 1 |
1000円札:
階級 | 度数 | 相対度数 | 累積相対度数 |
0~5 | 115 | 0.115 | 0.115 |
10 | 126 | 0.126 | 0.241 |
15 | 143 | 0.143 | 0.384 |
20 | 125 | 0.125 | 0.509 |
25 | 150 | 0.15 | 0.659 |
30 | 111 | 0.111 | 0.77 |
35 | 79 | 0.079 | 0.849 |
40 | 53 | 0.053 | 0.902 |
45 | 50 | 0.05 | 0.952 |
50 | 23 | 0.023 | 0.975 |
55 | 14 | 0.014 | 0.989 |
60 | 3 | 0.003 | 0.992 |
65 | 5 | 0.005 | 0.997 |
70 | 1 | 0.001 | 0.998 |
75 | 2 | 0.002 | 1 |
80 | 0 | 0 | 1 |
500円硬貨:
階級 | 度数 | 相対度数 | 累積相対度数 |
0~3 | 433 | 0.433 | 0.433 |
6 | 296 | 0.296 | 0.729 |
9 | 174 | 0.174 | 0.903 |
12 | 63 | 0.063 | 0.966 |
15 | 22 | 0.022 | 0.988 |
18 | 12 | 0.012 | 1 |
21 | 0 | 0 | 1 |
100円硬貨:
階級 | 度数 | 相対度数 | 累積相対度数 |
0~10 | 26 | 0.026 | 0.026 |
20 | 25 | 0.025 | 0.051 |
30 | 53 | 0.053 | 0.104 |
40 | 80 | 0.08 | 0.184 |
50 | 111 | 0.111 | 0.295 |
60 | 156 | 0.156 | 0.451 |
70 | 139 | 0.139 | 0.59 |
80 | 119 | 0.119 | 0.709 |
90 | 105 | 0.105 | 0.814 |
100 | 73 | 0.073 | 0.887 |
110 | 60 | 0.06 | 0.947 |
120 | 25 | 0.025 | 0.972 |
130 | 10 | 0.01 | 0.982 |
140 | 11 | 0.011 | 0.993 |
150 | 3 | 0.003 | 0.996 |
160 | 2 | 0.002 | 0.998 |
170 | 2 | 0.002 | 1 |
180 | 0 | 0 | 1 |
度数分布表から99%の区間を求めます。階級の中央値でもいいのですが、心配なので階級の最大値を採用します。
5000円札:15枚
1000円札:60枚
500円硬貨:18枚
100円硬貨:140枚
紙幣で払う確率を高めに設定しているので、お釣り用の100円玉がかなり必要となっています。
300円を200食となると、たくさんの小銭が動いて、最初にかなりの枚数を用意しなければいけないようです。
しかもこれは品物が1品の場合です。
想定する支払い確率でもかなり変動しますが、まあまあの目安になっているのではないでしょうか。