Lesson13 修了問題コードレビュー

受講生の中からA君、B君、C君、D君の4人のソースコードを講評をします。個性がでますね。

Aくんのソース

おおざっぱに分けて最後にふるいにかける方式ソース(enshu013_A.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ジャガイモをx個、ニンジンをy個
x_max = int(5000/300)
if x_max > int(8000/100):
    x_max = int(8000/100)
y_max = int(5000/110)
if y_max > int(8000/230):
    y_max = int(8000/230)
print("ジャガイモは"+str(x_max)+"個以下で"+"ニンジンは"+str(y_max)+"個以下です")
#for x in range(1,17):
    #print(str(x)+"個まで買えます")
xy_kumi=[]
#for X in xy_kumi :
    #print(X)
for x in range (1,17):
    for y in range (1,35):
        xy_kumi.append((x,y))
#print(xy_kumi)
S_max=0
for xy in xy_kumi :
    x=xy[0]
    y=xy[1]
    S=x+y
    if 300*x+110*y<=5000:
        if 100*x+230*y<8000:
            #print("条件を満たしました。")
            print(str(S))
            if S>S_max:
                S_max=S
print(str(S_max))

コメントが残っていて、なかなか格闘の跡が生々しいソースコードです。A君は、ジャガイモとニンジンの和は求められましたが、ジャガイモとニンジンはそれぞれ何個になるかはもう一歩でした。
まず、全体の方針は、ヒント通りに解いています。

注目するところは、

14
for x in range(0,17):

のところでしょうか、ここは17という計算した値でなく、x_maxを使うべきでした。
次のfor文も同じです。なぜならこの問題の設定を少しかえて重さを6500gにしたとき、もう一度計算して17という数字を変えなければいけません。ところが、変数においておけば、ソースのあまたで数か所書き換えるだけであとは手を入れなくてもよくなります。つまり、ソースコードが汎用なのです。

xy_kumiはざっと荒く絞り込みを行います。この段階ではまだ重さとお金の条件はクリアしないものも入っています。
そこから

19
for xy in xy_kumi :

のループであらためてふるいにかけているわけです。if文を二重にネストし、合致するものだけ和を計算するという風にしています。
A君はインデントについてもう一度勉強するといいでしょう。29行目のインデントをインデントなしか、1個のインデントか、2個のインデントかで迷っていましたね。表示する変数はどのループでの処理中のものを表示したいかということをもう一度ゆっくり考えてみるとよいと思います。
A君は高校1年生です。この後、線形計画法で解く方法も学んだあとに、総当たり方法と比較して両方の長所、短所について考えるとよいでしょう。

Bくんのソース

(enshu013_B.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
x_max=int(5000/300)
if x_max < (8000/100):
    x_max = int(8000/100)

y_max=int(5000/110)
 #ifも同じy_max < (8000/230)

xy_kumi =[]

for x in range(0,17):
    for y in range(0,46):
        if x*300+y*110 > 5000:
            continue
        if x*100+y*230 > 8000:
            continue
        xy_kumi.append([x,y])

#print(xy_kumi)
max=0
x_max=0
y_max=0
for k in xy_kumi:
    x=k[0]
    y=k[1]
    if max<x+y:
        max=x+y
        x_max=x
        y_max=y
print(str(max))
print(str(x_max))
print(str(y_max))
        #print(str(x)+" "+str(y))

B君もA君と同じ方針ですが、次のif 文は条件に合致する場合でなく、合致しないものをはじくという方法で、はじめに、xy_kumi[]を重さの条件もお金の条件もクリアしたものだけをセットしています。うまいですね。これだとあとは要素のx+yが最大のものを見つければよいだけです。
ちょっと惜しいのは、前半でx_max,y_maxという変数をおおまかな上限の意味で使っているのに、後半では x+yを暫定最大になる値の意味で使っています。変数をこのように別の意味で使うのは変数の再定義といって好ましくありません。Cくんのソースのようにx_ans,y_ansのように別の変数を使うべきでした。
それ以外は、全体的にすっきりとしています。
ちなみにB君は中学2年生の最年少受講生です。さらにプログラム初心者でした。

Cくんのソース

(enshu013_C.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
x_max = 5000/300
if x_max > 8000/100 :
    x_max = 8000/100

x_max = int(x_max) +1

y_max = 5000/110
if y_max > 8000/230 :
    y_max = 8000/230

y_max = int(y_max) +1

ans = 0
price = 0
weight = 0
x_ans = 1
y_ans = 1
for x in range(1,x_max) :
    for y in range(1,y_max) :
        if 300 * x + 110 * y > 5000 :
            continue
        if 100 * x + 230 * y > 8000 :
            continue
        if x + y > ans :
            ans = x + y
            x_ans = x
            y_ans = y
            weight = 300 * x + 110 * y
            price = 100 * x + 230 * y
            print("x+yの最大値が更新されました", end="")
            print("x= "+str(x)+" y= " +str(y) +" weight= " + str(weight) + " price= " +str(price))

print("ジャガイモは " + str(x_ans) + "ニンジンは " + str(y_ans) +"の時に和は最大になります。")
print("ans="+str(ans) +" weight=" + str(weight) + " price=" +str(price))

C君はヒントを使わず、独自の方法でforを二重にして解いています。A君やB君がfor文を2回並列に使っているのに対して、C君は1つのforの中でネストして2回使っています。
x_max , y_maxをうまく使っていますね。変数名がわかりにくかったのでこのソースコードはエッセンスでないところは先生の解答を混ぜてあります。

Dくんのソース

(enshu013_D.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
x_max = 5000/300
if x_max > 8000/100 :
    x_max = 8000/100
x_max = int(x_max)

y_max = 5000/110
if y_max > 8000/230 :
    y_max = 8000/230
y_max = int(y_max)

xy_kumi =[]
for x in range(0,x_max+1):
    for y in range(0,y_max+1):
        if x*300+y*110 > 5000:
            continue
        if x*100+y*230 > 8000:
            continue
        xy_kumi.append([x,y])

max=0
x_ans=0
y_ans=0
while xy_kumi :
    x_y = xy_kumi.pop()
    x = x_y[0]
    y = x_y[1]
    print("x=" +str(x)+" y="+str(y))
    if max < x + y:
        max = x + y
        x_ans = x
        y_ans = y

print(str(max))
print(str(x_ans))
print(str(y_ans))

最後に先生が予想していなかったソースです。D君が書いたものですが、D君はこの問題を見たときになぜかwhileを使うとひらめいたそうです。前半はB君と同じで、重さとお金の条件から、条件だを満たす[x,y]の組を過不足なくリストアップしています。
特徴的なのは、後半です。みんながforループを使っているのに対してwhileを使っています。しかも、whileの中はxy_kumi です。これは、xy_kumi がある限り、つまりxy_kumiの長さが0にならない限り、という意味になります。真偽の意味をよく理解しています。リストがpop関数によってだんだん短くなり、長さが0になるとwhileループを抜けるという考え方です。
ヒントはTAの先生にもらったようですが、だんだん短くしていくという発想はD君が考えたそうです。ユニークな視点です。

先生が用意していたソースその1

先生が用意していたソースはA君とB君を半分ずつ使ったようなコードです。まず荒くx,yを絞り込み、その後、重さの条件とお金の条件で条件に合わないものをcontinueではじいていきます。
他の人が分かるようにコメント入れていることに注意してください。自分だけがわかるのではなく、他人にもわかりやすくする工夫が必要です。

(enshu013_sensei.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
###########################################
# ここでは、大雑把に、xとyがそれぞれ独立に動いたときに最大値として何個までいけるかを計算して目安をつけます。
x_max = int(5000/300)
if x_max > int(8000/100) :
    x_max = int(8000/100)

y_max = int(5000/110)
if y_max > int(8000/230) :
    y_max = int(8000/230)

###########################################
# #ニンジンx個 ジャガイモy個 として
# xとyの組を列挙して、2次元のリストxy_kumiに入れていきます。
xy_kumi =[]
for x in range(1,x_max +1) :
    for y in range(1,y_max+1) :
        #print(str(x) + ',' + str(y))
        xy_kumi.append([x,y])

#この行はデバッグのためです後で消しましょう。
#print(xy_kumi)

###########################################
# xy_kumiの中からお金の条件と重さの条件をクリアできるx,yの組に絞って、x+yの最大値を見つけます。
ans = 0
price = 0
weight = 0
x_ans = 0
y_ans = 0
for pair in xy_kumi :
    x = pair[0]
    y = pair[1]
    if 300 * x + 110 * y > 5000 :
        continue
    if 100 * x + 230 * y > 8000 :
        continue
    if x + y > ans :
        ans = x + y
        x_ans = x
        y_ans = y
        weight = 300 * x + 110 * y
        price = 100 * x + 230 * y
        print("x+yの最大値が更新されました", end="")
        print("x= "+str(x)+" y= " +str(y) +" weight= " + str(weight) + " price= " +str(price))

print("ジャガイモは " + str(x_ans) + "ニンジンは " + str(y_ans) +"の時に和は最大になります。")
print("ans="+str(ans) +" weight=" + str(weight) + " price=" +str(price))

参考までに、先生が教育的な配慮をせず、コメントも習っていない書き方も使って軽く書いたコードを載せておきます。三項演算子というifを1行で書けるような書き方を使っています。
ずいぶんと短いと感じると思います。

(enshu013_sensei2.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
x_max = int(5000/300)+1 if x_max > 8000/100 else int(8000/100)
y_max = int(5000/110)+1 if y_max < 8000/230 else int(8000/230)

ans = 0
price = 0
weight = 0
x_ans = 0
y_ans = 0
for x in range(1, x_max+1):
    for y in range(1, y_max+1):
        if 300 * x + 110 * y > 5000 or 100 * x + 230 * y > 8000:
            continue

        if x + y > ans:
            ans = x + y
            x_ans = x
            y_ans = y
            weight = 300 * x + 110 * y
            price = 100 * x + 230 * y
            # print("x= "+str(x)+" y= " +str(y) +" weight= " + str(weight) + " price= " +str(price))

print("ジャガイモは " + str(x_ans) + "ニンジンは " + str(y_ans) + "の時に和は最大になります。")
print("ans="+str(ans) + " weight=" + str(weight) + " price=" + str(price))

さあ、皆さんどうだったでしょうか?4人のソースコードを紹介しましたが、自分だったらこんな風に書く、というコードがあればこちらよりホームページ編集部までご連絡をください。紹介します。