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

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

ただいまの
回答率

90.75%

  • Python 3.x

    5295questions

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

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

解決済

回答 5

投稿

  • 評価
  • クリップ 2
  • VIEW 267

目標)
以下のルールの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)))
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+4

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

  • 前提が必ず成立するように初手sortedを追加しました。
  • どうせリストにするのなら見にくいmapの使用をやめました。
ps1 = sorted([int(x)-1 for x in input().split()])
ps2 = sorted([int(x)-1 for x in input().split()])
es = [int(x) for x in input().split()]
fs = [int(x) for x in input().split()]

def play(p, s):
    if s[0] < s[1]:
        return min(p), max(p)
    else:
        return max(p), min(p)

rs1 = play(ps1, [es[p] for p in ps1])
rs2 = play(ps2, [es[p] for p in ps2])

rs = play((rs1[0], rs2[0]), fs)
print(rs[0]+1)
print(rs[1]+1)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/16 14:42

    お返事ありがとうございます。

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

    キャンセル

  • 2018/04/16 18: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をそれぞれ足してあげれば、ほしかった出力が得られます。

    キャンセル

  • 2018/04/17 13:03

    補足説明ありがとうございます。

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

    ご指導ありがとうございました。

    キャンセル

checkベストアンサー

+3

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

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

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

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

 入力値のパース

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

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

 データの構造化

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

games1 = ({p_1: e_1, p_2: e_2}, {p_3: e_3, p_4: e_4})

def winner(**scores):
    best = min(scores.values())
    for player in scores:
        if scores[player] == best:
            return player
    else:
        pass  # 同着は絶対にないものとする

game2_players = [winner(**game) for game in games1]

 出力値の計算 & 出力

あとはおまかせします。

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 23:27

    お返事ありがとうございます。
    私も書きながら、長い、読みづらいだろうなと思っていました。
    バカ正直なコードしか書けないですが、期待値を求められるようになったことに楽しさを感じています。

    一方で、教えて頂いたようにスマートなコードを書けるように成長したいです。
    そのため、コード問題を何度も解いています。
    特に解答が参照できる以下のサイトを利用しています。

    参考サイト 
    https://www.practicepython.org/
    https://codeclubprojects.org/en-GB/python/

    上記のトレーニングのおかげで、本件の解答のような単純なコードはかけるようになりました。
    更なるステップアップのために、すべきことなどご教授頂ければ嬉しいです。

    キャンセル

  • 2018/04/16 00:12

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

    キャンセル

  • 2018/04/16 14:40

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

    キャンセル

+2

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

def get_winner(record, player1, player2):
    if record[player1] < record[player2]:
        return player1

    return player2


def read_record(player_names):
    return {
        p: int(r) for p, r in zip(player_names, input().split())
    }


cards = [
    input().split() for _ in range(2)
]

first_record = read_record('1234')
first_winner = sorted(
    [get_winner(first_record, *players) for players in cards], key=int
)

second_record = read_record(first_winner)
print(
    *sorted(second_record, key=lambda k: second_record[k]), sep='\n'
)

Wandbox

要件によっては

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

def get_winner(record, player1, player2):
    if record[player1] < record[player2]:
        return player1

    return player2


def read_record(player_names):
    return {
        p: int(r) for p, r in zip(player_names, input().split())
    }


cards = [
    input().split() for _ in range(2)
]

players = list('1234')
while len(players) != 1:
    record = read_record(players)
    players = [get_winner(record, *player_pair) for player_pair in cards]

    cards = list(
        zip(*[iter(players)]*2)
    )
    players = sorted(players, key=int)

print(*players)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 23:16

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

    キャンセル

+2

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

$ cat times
1 3
2 4
600 650 500 700
550 600
3 2
4 1
833 897 901 925
870 855
$ 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}'
2 3
2 1

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/16 14:41

    お返事ありがとうございます。

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

    キャンセル

+1

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

group1 = [int(x)-1 for x in input().split()]
group2 = [int(x)-1 for x in input().split()]

first = [int(x) for x in input().split()]
second = [int(x) for x in input().split()]

# ここからミソ
first_result = sorted(enumerate(first), key=lambda x:x[1])

winners_list = []
for group in [group1, group2]:
    for i, time in first_result:
        if i in group:
            winners_list.append(i)
            break
winners_list.sort()
# ここまで

for time, i in sorted(zip(second, winners_list), key=lambda x:x[0]):
    print(i+1)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 23:56

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

    キャンセル

  • 2018/04/16 14:41

    お返事ありがとうございます。

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

    キャンセル

  • 2018/04/17 13: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]が関係しているのかが理解できません。

    お時間があるときで構いませんので、お返事頂けたら幸いです。

    キャンセル

  • 2018/04/17 13: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しています。これで各グループで一番早い人がわかります。

    キャンセル

  • 2018/04/17 13:32 編集

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

    キャンセル

  • 2018/04/17 14:15

    お返事頂きありがとうございます。
    大変失礼な質問をしてしまし、申し訳ございません。
    今後は、出来る限り自己解決できるよう努めます。

    詳細な説明のおかげですべて理解することができました。
    お時間割いて頂きありがとうございます。
    ご指導ありがとうございました。

    キャンセル

  • 2018/04/17 14:18

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

    キャンセル

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

  • ただいまの回答率 90.75%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    5295questions

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