Pythonで学ぶアルゴリズム　第2弾：自動販売機

# #Pythonで学ぶアルゴリズム< 自動販売機のお釣り >#

##はじめに
基本的なアルゴリズムをPythonで実装し，アルゴリズムの理解を深める．
その第2弾として自動販売機のお釣り計算を扱う．

##自動販売機
まずここでいう自動販売機を定義しておく．
日本の紙幣・硬貨をすべて扱え，投入金額が不足しているときは購入できないものである．
尚，投入金額と購入金額についてはユーザが入力することを想定している．

##自動販売機 ver.1
単純にお釣りを計算してくれる自動販売機機能を作る．不足などは考慮しない．
次のようなコードとなった．
####コード

```python:calc_change1.py
"""
2020/12/15
@Yuya Shimizu

自動販売機
お釣りの計算 ver.1
とりあえず，お釣りを計算する
"""
# 単純なお釣りの計算：お釣り = 投入金額 - 購入金額
insert_price = input('投入金額 >> ')
product_price = input('購入金額 >> ')

change = int(insert_price) - int(product_price)

print('お釣り　 >> {}円'.format(change))

```

####実行結果

```
投入金額 >> 20000     #ユーザの入力
購入金額 >> 1864      #ユーザの入力
お釣り　 >> 18136円   #計算結果の表示
```

お釣りは計算できるが，どのようにお釣りを返すのだろうか？
できるだけ紙幣硬貨の枚数を少ないようにお釣りを返すの好ましい．
ver.2では，できるだけ少ない紙幣硬貨でお釣りを返す．

##自動販売機 ver.2
できるだけ少ない紙幣硬貨でお釣りを返す自動販売機機能を作る．不足などは考慮しない．
次のようなコードとなった．
####コード

```python:calc_change2.py
"""
2020/12/15
@Yuya Shimizu

自動販売機
お釣りの計算 ver.2
できるだけ少ない紙幣硬貨でお釣りを返す
"""
insert_price = input('投入金額 >> ')
product_price = input('購入金額 >> ')

# 単純なお釣りの計算：お釣り = 投入金額 - 購入金額
change = int(insert_price) - int(product_price)
total_change = change

# できるだけ少ない枚数でお釣りを返す
cahnge_10000 = change//10000   # 一万円札の枚数
change = change%10000             
cahnge_5000 = change//5000      # 五千円札の枚数
change = change%5000
cahnge_1000 = change//1000      # 千円札の枚数
change = change%1000
cahnge_500 = change//500         # 五百円玉の枚数
change = change%500
cahnge_100 = change//100        # 百円玉の枚数
change = change%100
cahnge_50 = change//50           # 五十円玉の枚数
change = change%50
cahnge_10 = change//10           # 十円玉の枚数
change = change%10
cahnge_5 = change//5              # 五円玉の枚数
change = change%5
cahnge_1 = change//1              # 一円玉の枚数


print('お釣り {}円\n'.format(total_change))
print('10000円札：{}枚\n5000円札：{}枚\n1000円札：{}枚\n500円玉：{}枚\n100円玉：{}枚\n50円玉：{}枚\n10円玉：{}枚\n5円玉：{}枚\n1円玉：{}枚\n'
      .format(cahnge_10000, cahnge_5000, cahnge_1000, cahnge_500, cahnge_100, cahnge_50, cahnge_10, cahnge_5, cahnge_1))


```

####実行結果

```
投入金額 >> 20000
購入金額 >> 1423
お釣り 18577円

10000円札：1枚
5000円札：1枚
1000円札：3枚
500円玉：1枚
100円玉：0枚
50円玉：1枚
10円玉：2枚
5円玉：1枚
1円玉：2枚
```

できるだけ紙幣硬貨の枚数を少ないようにお釣りを返すことができた．
プログラムを見ると，紙幣硬貨の枚数計算は同じことの繰り返しである．
ver.3では，同じことの繰り返しをまとめて，コードを単純化する．

##自動販売機 ver.3
リストを使って，先ほどのコードを単純化する．
次のようなコードとなった．
####コード

```python:calc_change3.py
"""
2020/12/15
@Yuya Shimizu

自動販売機
お釣りの計算 ver.3
コードを単純化する
"""
insert_price = input('投入金額 >> ')
product_price = input('購入金額 >> ')

# 単純なお釣りの計算：お釣り = 投入金額 - 購入金額
change = int(insert_price) - int(product_price)
total_change = change

# 紙幣硬貨をリストにまとめて，コードをまとめる
money_list = [10000, 5000, 1000, 500, 100, 50, 10, 5, 1]

print('お釣り {}円\n'.format(total_change))
# できるだけ少ない枚数でお釣りを返す
for money in money_list:
    change_money = change//money
    change = change%money
    print('{}円札：{}枚'.format(money, change_money))

```

####実行結果

```
投入金額 >> 20000
購入金額 >> 1423
お釣り 18577円

10000円札：1枚
5000円札：1枚
1000円札：3枚
500円玉：1枚
100円玉：0枚
50円玉：1枚
10円玉：2枚
5円玉：1枚
1円玉：2枚
```

実行結果は変わらず，コードは非常に短くでき，単純化することができた．
いよいよ，ver.4では不足を含めてエラーに対応する自動販売機機能を作る．

##自動販売機 ver.4
ユーザ入力時には金額として正しい入力を想定しているが，間違えて数字ではなく文字を入力してしまうかもしれない．また，投入金額が購入金額に対して不足している場合もあるかもしれない．このような場合に，エラーメッセージを送り，プログラムを強制終了させたい．
ここでは，```isdecimal()```という文字列がすべて数字で構成されているかを判定できる文字列のメソッドでユーザ入力情報が正しいかを判定し，強制終了には```sys.exit()```を用いる．
次のようなコードとなった．
####コード

```python:calc_change4.py
"""
2020/12/15
@Yuya Shimizu

自動販売機
お釣りの計算 ver.4
エラーに対応する
"""
import sys

# 紙幣硬貨をリストにまとめて，コードをまとめる
money_list = [10000, 5000, 1000, 500, 100, 50, 10, 5, 1]

insert_price = input('投入金額 >> ')
# 入力が整数でないと強制終了
if not insert_price.isdecimal():    #文字列を数字だけで構成されている(整数)かをチェックできる
    print('整数を入力してください')
    sys.exit() #強制プログラム終了
    
product_price = input('購入金額 >> ')
# 入力が整数でないと強制終了
if not product_price.isdecimal():
    print('整数を入力してください')
    sys.exit()
    
# 単純なお釣りの計算：お釣り = 投入金額 - 購入金額
change = int(insert_price) - int(product_price)

# お釣りがマイナスとなる場合に強制終了
if change < 0:
    print('投入金額が不足しています')
    sys.exit()

print('お釣り {}円\n'.format(change))
# できるだけ少ない枚数でお釣りを返す
for money in money_list:
    change_money = change//money
    change %= money
    print('{}円札：{}枚'.format(money, change_money))


```

####実行結果

```
========= 実行結果1： 投入金額に数字以外を入力した場合=========
投入金額 >> 5.5
整数を入力してください
# 強制終了
========= 実行結果2：購入金額に数字以外を入力した場合 =========
投入金額 >> 5
購入金額 >> あ
整数を入力してください
# 強制終了
========= 実行結果3：投入金額が不足している場合 =========
投入金額 >> 5
購入金額 >> 150
投入金額が不足しています
# 強制終了
========= 実行結果4：正常な入力の場合 =========
投入金額 >> 500
購入金額 >> 150
お釣り 350円

10000円札：0枚
5000円札：0枚
1000円札：0枚
500円札：0枚
100円札：3枚
50円札：1枚
10円札：0枚
5円札：0枚
1円札：0枚
```

実行結果1~3では正常でない入力や条件となった場合の出力を示している．結果から分かるように，うまく対処できている．これで，エラーにも対応した自動販売機機能を作ることができた．

##感想
自動販売機機能の仕組みはある程度知っていたが，今回プログラムで用いたisdecimalやsysの関数については初めて用いる良い機会となった．特にsysはあまり使ったことがなく，いまいち知らない．今後勉強できたらと思う．また，改めてリストでまとめたりとコードの単純化についても再確認できた．

##参考文献
Pythonで始めるアルゴリズム入門　伝統的なアルゴリズムで学ぶ定石と計算量
　　　　　　　　　　　　　　　　　　　　　　　　 増井 敏克　著　　翔泳社