質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

5回答

1970閲覧

Python3  マラソン大会の結果 条件分岐 ループ処理 スマートなコードの書き方

python3_beginer

総合スコア46

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

2クリップ

投稿2018/04/15 13:27

目標)
以下のルールの1位と2位のゼッケンナンバーを求めたい

ルール

1 から 4 までのゼッケンナンバーをもつ選手 4 人が、短距離走大会に参加します。
1 回戦では、参加者は 2 組にずつ競争します。
各組で上位となった 2 人が 2 回戦に進出します。
2 回戦の勝者がこの大会の優勝者です。

まず 1 回戦の組み分けが与えられます。
そして 1 回戦の各参加者のタイム (秒) が、続いて 2 回戦の各参加者のタイム (秒) がゼッケンナンバーの小さい順に与えられるので一位と二位を求めたい。

なお、1 回戦、2 回戦における各選手のタイムはすべて異なり、同着は絶対にないものとする。

1 回戦の組み分け: 1 vs 3, 2 vs 4

1 回戦のタイム:
1 → 600秒
2 → 650秒
3 → 500秒
4 → 700秒

このとき 1 vs 3 では 3 の勝利、 2 vs 4 では 2 の勝利となります。
したがって 2 回戦は 2 vs 3 で争われます。

2 回戦のタイム:
2 → 550秒
3 → 600秒

2 回戦の勝者は 2 となり、優勝者は 2、準優勝者は 3 となります。
この例は下の入力例 1 に対応しています。

#例イメージ図

イメージ説明

・1 行目に選手のゼッケンナンバーを表す整数 p_1, p_2 、
2 行目に同様の整数 p_3, p_4 が半角スペース区切りで与えられます。
このとき 1 回戦の組み分けが p_1 vs p_2, p_3 vs p_4 となります。

・次の行には各参加者の 1 回戦のタイムを表す ### 4 つの整数がエントリーナンバーの小さい順に半角スペース区切りで与えられます。
これらの整数を e_1, e_2, e_3, e_4 で表します。

・次の行には 2 回戦進出者の 2 回戦のタイムを表す**### 2 つの整数がエントリーナンバーの小さい順に半角スペース区切りで与えられます。**
これらの 整数を f_1, f_2 で表します。
・入力は合計で 4 行となり、入力値最終行の末尾に改行が1つ入ります。

入力例1
1 3
2 4
600 650 500 700
550 600

出力例1
2
3

入力例2
3 2
4 1
833 897 901 925
870 855

出力例2
2
1

私のコード)

期待値通りの出力ができると思います。
ただ、独学初心者のため、コードのスマートさに欠けるのではないかと不安です。
内包表記、関数やクラスを利用してよりスマートな書き方があれば、教えて頂きたいです。
宜しくお願い致します。

コード #1回戦の選手の組み合わせのリスト player_data = [ list(map(int,input().split())) for _ in range(2) ] #1回戦の選手の組み合わせのリストからそれぞれのゼッケンナンバー抽出 a,b = [ x for x in player_data[0]] c,d = [x for x in player_data[1]] #一回戦のタイム結果、二回戦のタイム結果 リスト作成 #なお、二回戦のタイム結果は、勝者二人のゼッケンナンバーが小さい方から二つタイムが与えられる game_result = [ list(map(int,input().split())) for x in range(2)] #print(*game_result) #上記のタイム結果のリストそれぞれに、初項に0 を代入 #リストの要素ひとつ追加することで、ゼッケンナンバー=リストの項数 にするため game_result[0].insert(0,0) game_result[1].insert(0,0) #print(game_result) #一回戦のタイム結果、二回戦のタイム結果をgame_1, game_2の変数に代入 game_1, game_2 = game_result[0], game_result[1] #一回戦目の勝者二人を抽出 winner_1 = [] if game_1[a] > game_1[b]: winner_1.append(b) else: winner_1.append(a) if game_1[c] > game_1[d]: winner_1.append(d) else: winner_1.append(c) #print(winner_1) #上記の勝者二人を、ゼッケンナンバーを降順で並び変え #二回戦のタイム結果は、勝者二人のゼッケンナンバーが小さい方から二つタイムが与えられるから sorted_winnter_1 = sorted(winner_1) #print(sorted_winnter_1) #二回戦の選手2人のそれぞれのゼッケンナンバーを取得 e,f = sorted_winnter_1[0], sorted_winnter_1[1] #print(e,f) #二回戦目の優勝者、準優勝者二人のリスト作成 winner_2 = [] if game_2[0] > game_2[1]: winner_2.append(f) winnter_2.append(e) else: winner_2.append(e) winner_2.append(f) winner_2 = list(map(str,winner_2)) print('\n'.join(map(' '.join,winner_2)))
umyu👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

誤りの前提から来るバグを仕込んでいました。
修正点:

  • 前提が必ず成立するように初手sortedを追加しました。
  • どうせリストにするのなら見にくいmapの使用をやめました。

python

1ps1 = sorted([int(x)-1 for x in input().split()]) 2ps2 = sorted([int(x)-1 for x in input().split()]) 3es = [int(x) for x in input().split()] 4fs = [int(x) for x in input().split()] 5 6def play(p, s): 7 if s[0] < s[1]: 8 return min(p), max(p) 9 else: 10 return max(p), min(p) 11 12rs1 = play(ps1, [es[p] for p in ps1]) 13rs2 = play(ps2, [es[p] for p in ps2]) 14 15rs = play((rs1[0], rs2[0]), fs) 16print(rs[0]+1) 17print(rs[1]+1)

投稿2018/04/15 15:38

編集2018/04/15 21:38
mkgrei

総合スコア8560

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

python3_beginer

2018/04/16 05:42

お返事ありがとうございます。 私の知識不足で初見では理解できないところもあるので、時間をかけて吸収したいと思います。 ご指導ありがとうございます。
mkgrei

2018/04/16 09:41

play関数は2つの引数を取って、それぞれ2つの要素を持っているリストです。 1個目はプレイヤーの番号、2個目はそのラウンドのプレイヤーのスコアです。 スコアは、番号が若い順に並んでいることを仮定します。 なので、1個目が2個目より小さければ、番号が若いものが勝っていることになり、それ以外なら番号が大きい方が勝つことになります。 それを勝ったものの番号を先、負けたものの番号を2番目に返り値としています。 ps1を読み込む時に1を引いておくことで、そのまま、リストのインデックスになります。 [es[p] for p in ps1] はps1が若いもの順にソートされているのであれば、若い番号順のスコアを取り出します。 なので、ps1を読み込む時にsortedをしておきます。 ps2も同様。 ここまで用意しておけば、第一ラウンドは、 rs1 = play(ps1, [es[p] for p in ps1]) rs2 = play(ps2, [es[p] for p in ps2]) となり、rs1[0]とrs2[0]がそれぞれのラウンドの勝者になります。 その2つの番号をリストとしてfsと一緒に与えることで、 rs = play((rs1[0], rs2[0]), fs) のrs[0]が優勝者番号、rs[1]が準優勝者番号になります。 fsは問題の与え方により若い番号順のスコアであることに注意。 最後に読み込み時に引いた1をそれぞれ足してあげれば、ほしかった出力が得られます。
python3_beginer

2018/04/17 04:03

補足説明ありがとうございます。 説明のおかげで、理解することができました。 今後は何度も書いて、自然とかけるようになるまで練習したいと思います。 ご指導ありがとうございました。
guest

0

ベストアンサー

※けなす意図はなく正直に「どうすると良くなるだろう」を記載します。

スマートかどうか以前に「長い」「読みにくい」という印象を受けました。多分、コードが機能単位に分割されていないからだと思います。

この手の問題では入力から導き出される出力を求められますので、まずは以下のように機能分割すると良いと思います。

入力->入力値のパース->データの構造化->出力値の計算->出力

入力値のパース

入力をバカ正直にパースすると以下のようなコードになります。これをどこか一箇所に固めておくだけでも少々読みやすくなると思います。

python

1p_1, p_2 = input().split() # 選手1組目のゼッケン 2p_3, p_4 = input().split() # 選手2組目のゼッケン 3e_1, e_2, e_3, e_4 = map(int, input().split()) # 1回戦のタイム 4f_1, f_2 = map(int, input().split()) # 2回戦のタイム

データの構造化

これは後処理を意識しながらどのようなデータ構造にするか決めるんですが、まぁここは好き好きです。私なら以下のようなデータ構造にして別途winner(**scores)のような関数を用意します。

python

1games1 = ({p_1: e_1, p_2: e_2}, {p_3: e_3, p_4: e_4}) 2 3def winner(**scores): 4 best = min(scores.values()) 5 for player in scores: 6 if scores[player] == best: 7 return player 8 else: 9 pass # 同着は絶対にないものとする 10 11game2_players = [winner(**game) for game in games1]

出力値の計算 & 出力

あとはおまかせします。

追記:コードのデバッグはしてませんのでバグが残ってたらすいません。

投稿2018/04/15 14:05

編集2018/04/15 14:08
YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

python3_beginer

2018/04/15 14:27

お返事ありがとうございます。 私も書きながら、長い、読みづらいだろうなと思っていました。 バカ正直なコードしか書けないですが、期待値を求められるようになったことに楽しさを感じています。 一方で、教えて頂いたようにスマートなコードを書けるように成長したいです。 そのため、コード問題を何度も解いています。 特に解答が参照できる以下のサイトを利用しています。 参考サイト  https://www.practicepython.org/ https://codeclubprojects.org/en-GB/python/ 上記のトレーニングのおかげで、本件の解答のような単純なコードはかけるようになりました。 更なるステップアップのために、すべきことなどご教授頂ければ嬉しいです。
YouheiSakurai

2018/04/15 15:12

ここで回答する側に回ると良いですよ。自然と多くの事に触れられますし。あとはなんでも良いので常に書く事でしょうね。
python3_beginer

2018/04/16 05:40

アドバイスありがとうございます。 とにかく書いて書いて書きまくります。 知識に自信がついてきたら、解答する側にもチャレンジしてみます。
guest

0

Pythonなど不要。そう、シェル芸ならね!

bash

1$ cat times 21 3 32 4 4600 650 500 700 5550 600 63 2 74 1 8833 897 901 925 9870 855 10$ cat times|xargs -n10|awk '{t[1]=$5;t[2]=$6;t[3]=$7;t[4]=$8;if(t[$1]<t[$2]){a=$1}else{a=$2};if(t[$3]<t[$4]){b=$3}else{b=$4};print a,b,$9,$10}'|awk '{if($1<$2){i=1;j=2}else{i=2;j=1};n[$1]=i;n[$2]=j;t[1]=$3;t[2]=$4;if(t[n[$1]]<t[n[$2]]){a=$1;b=$2}else{a=$2;b=$1};print a,b}' 112 3 122 1

投稿2018/04/15 21:19

hichon

総合スコア5737

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

python3_beginer

2018/04/16 05:41

お返事ありがとうございます。 私の知識不足で初見では理解できないところもあるので、時間をかけて吸収したいと思います。 ご指導ありがとうございます。
guest

0

書いてみました。汎用性はあんまりないかもしれません。

Python

1def get_winner(record, player1, player2): 2 if record[player1] < record[player2]: 3 return player1 4 5 return player2 6 7 8def read_record(player_names): 9 return { 10 p: int(r) for p, r in zip(player_names, input().split()) 11 } 12 13 14cards = [ 15 input().split() for _ in range(2) 16] 17 18first_record = read_record('1234') 19first_winner = sorted( 20 [get_winner(first_record, *players) for players in cards], key=int 21) 22 23second_record = read_record(first_winner) 24print( 25 *sorted(second_record, key=lambda k: second_record[k]), sep='\n' 26)

Wandbox

要件によっては

単に一位を出力すればいいならもうちょっとスマートになるんですがね。

Python

1def get_winner(record, player1, player2): 2 if record[player1] < record[player2]: 3 return player1 4 5 return player2 6 7 8def read_record(player_names): 9 return { 10 p: int(r) for p, r in zip(player_names, input().split()) 11 } 12 13 14cards = [ 15 input().split() for _ in range(2) 16] 17 18players = list('1234') 19while len(players) != 1: 20 record = read_record(players) 21 players = [get_winner(record, *player_pair) for player_pair in cards] 22 23 cards = list( 24 zip(*[iter(players)]*2) 25 ) 26 players = sorted(players, key=int) 27 28print(*players)

投稿2018/04/15 13:52

編集2018/04/15 23:04
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

python3_beginer

2018/04/15 14:16

お返事ありがとうございます。 今はループ処理を書くのが限界なので、とても参考になります。 最初は見ながら書き写して、知識にしていきたいと思います。 ご指導ありがとうございます。
guest

0

質問者様のコード自体よりむしろ問題のロジックがスマートじゃなくて、腹が立ったのでスマートなロジックで書いた例。

python

1group1 = [int(x)-1 for x in input().split()] 2group2 = [int(x)-1 for x in input().split()] 3 4first = [int(x) for x in input().split()] 5second = [int(x) for x in input().split()] 6 7# ここからミソ 8first_result = sorted(enumerate(first), key=lambda x:x[1]) 9 10winners_list = [] 11for group in [group1, group2]: 12 for i, time in first_result: 13 if i in group: 14 winners_list.append(i) 15 break 16winners_list.sort() 17# ここまで 18 19for time, i in sorted(zip(second, winners_list), key=lambda x:x[0]): 20 print(i+1)

投稿2018/04/15 14:44

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hayataka2049

2018/04/15 14:56

これ、見た目は綺麗だけど無駄な計算をしないという意味ではかえって冗長です。気にしてませんが
python3_beginer

2018/04/16 05:41

お返事ありがとうございます。 私の知識不足で初見では理解できないところもあるので、時間をかけて吸収したいと思います。 ご指導ありがとうございます。
python3_beginer

2018/04/17 04:12

大変恐縮ですが、質問がございます。 # ここからミソ first_result = sorted(enumerate(first), key=lambda x:x[1]) 出力 [(1, 876), (3, 906), (2, 921), (0, 988)] key=lambda x:x[1] こちらの式が、1行目、2行目に与えられたゼッケンナンバーを抽出していると思いますが、なぜ、x[1]が関係しているのかが理解できません。 お時間があるときで構いませんので、お返事頂けたら幸いです。
hayataka2049

2018/04/17 04:30

そういうときはよくわからない関数をググったり、インタプリタで動かして確認し、ご自身で理解されるのが望ましいです。あまり他人に頼りすぎないように。今回は書きます。 >>> s = "600 650 500 700" >>> first = [int(x) for x in s.split()] >>> first [600, 650, 500, 700] >>> list(enumerate(first)) [(0, 600), (1, 650), (2, 500), (3, 700)] >>> first_result = sorted(enumerate(first), key=lambda x:x[1]) >>> first_result [(2, 500), (0, 600), (1, 650), (3, 700)] enumerateは[(index, value),...]という形で返す関数。 これをvalueの方でソートしています。つまり、firstの結果を速い順に並べていますが、本当に欲しいのは番号(インデックス)が早い順に並んだリストです。 それからgroup1とgroup2をリストにしてループしているforがあり、first_resultはこの内側のループで利用しています。 これはインデックス(と、必要ないけど入っている時間)が早い順に並んだリストなので、先頭から見ていって最初にgroupにあったものをwinners_listにappendしています。これで各グループで一番早い人がわかります。
hayataka2049

2018/04/17 04:32 編集

Aさん、Bさん、Cさん、DさんがいてDCBAの順に速いとし、ACとBDでグループを組んだとして、 1.まずACグループについて見る 2.速い順リストをD,C,と見て先にCがhitした(速い順リストにあった)のでCが速い 3.次にBDグループについて見る 4.速い順リストをD,と見て先にDがhitしたので以下略 ご理解いただけたでしょうか。
python3_beginer

2018/04/17 05:15

お返事頂きありがとうございます。 大変失礼な質問をしてしまし、申し訳ございません。 今後は、出来る限り自己解決できるよう努めます。 詳細な説明のおかげですべて理解することができました。 お時間割いて頂きありがとうございます。 ご指導ありがとうございました。
hayataka2049

2018/04/17 05:18

べつに失礼という訳じゃないんですが、答える方も大変ですし、いろいろ理解するためには自分で動かして動作を追った方が良いですからそう書いた次第です。お気遣いなく
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問