プログラミング
記事内に商品プロモーションを含む場合があります

Python初級:スコアから金・銀・銅メダルを判定する2つのプログラムを解説

Aru

競技会などで「スコアに応じて金・銀・銅メダルを付与したい」とき、順位の付け方によって結果が変わることがあります。本記事では、Pythonで「順位ベース」と「スコアベース」2種類のメダル授与ルールを実装し、それぞれの違いをサンプルコードで解説します

問題

問題文

次のような問題を考えます。

問題

与えられた参加者スコアのリストから、メダル授与の異なる2つのルールに基づいて、金・銀・銅メダルを獲得する参加者を決定してください。

🥇 ルール 1:順位ベースのメダル授与

1位に金メダル、2位に銀メダル、3位に銅メダルを付与します。ただし、同じスコアの人が複数人いる場合は、同順位とします。たとえば、100,100,90,80というスコアの場合、1位、1位、3位、4位とし、メダルは、金、金、銅、メダルなしとなります。

🥇 ルール 2:スコアベースのメダル授与

スコアが1位に金メダル、2位に銀メダル、3位に銅メダルを付与します。この場合は、100,100,90,80というスコアの場合、1位、1位、2位、3位とし、メダルは金、金、銀、銅となります。

入力は参加者のスコアリストです

20 30 40 50 60 20 30 90 100 90 90

出力結果は、各参加者のメダルの色(GOLD, SILVER, BRONZE, 授与されない場合はNONE)とします。例の場合は、以下のようになります。

NONE
NONE
NONE
NONE
NONE
NONE
NONE
SILVER
GOLD
SILVER
SILVER

ルール1の解法

ルール1の解答は以下のようになります。

score = list(map(int, input().split()))

n = len(score)

persons = [(i, score[i]) for i in range(n)]

persons.sort(key=lambda x: x[1], reverse=True)


ans = [0 for _ in range(n)]
cur = persons[0][1]
num = 0
for i in range(n) : 
    if cur != persons[i][1] :
       num = i
    cur = persons[i][1] 
    ans[persons[i][0]] = num

for e in ans :
    if e == 0 :
        print("GOLD")
    elif e == 1 :
        print("SILVER")
    elif e == 2 :
        print("BRONZE")
    else :
        print("NONE")

入力処理

標準入力から、データの読み込みを行います。

score = list(map(int, input().split()))

n = len(score)
  • input().split() で、スペース区切りのスコアを文字列リストとして読み込みます(例: '20 30 40' → ['20', '30', '40'])。※この時点では文字列です
  • map(int, ...)で、文字列リストの要素全てを整数に変換します
  • list(...)で、それを整数リストとしてscoreに格納します
  • n=len(score)で、参加者の人数を計算しています

データ整形

persons = [(i, score[i]) for i in range(n)]

このまま score リストをソートしてしまうと、誰が何点だったのか(元の順番)がわからなくなってしまいます。そこで、(参加者番号(インデックス), スコア) というペア(タプル)のリスト persons を新しく作り、ソートしてもどの参加者のスコアだったかわかるようにしています。

score = [20, 100, 90] だった場合、 persons = [(0, 20), (1, 100), (2, 90)] となります

ソート

persons.sort(key=lambda x: x[1], reverse=True)

persons リストをソートしています。key=lambda x: x[1] という指定は、「リストの各要素(x)の、2番目(インデックス 1)の値、つまりスコアを基準に並べ替えてください」という意味です。reverse=True は、降順(スコアが高い順)に並べ替える指定です。

persons = [(0, 20), (1, 100), (2, 90)] だった場合、 このブロック実行後は persons = [(1, 100), (2, 90), (0, 20)] となります。

順位決定

ans = [0 for _ in range(n)]
cur = persons[0][1]
num = 0
for i in range(n) : 
    if cur != persons[i][1] :
       num = i
    cur = persons[i][1] 
    ans[persons[i][0]] = num

ルール1(1位、1位、3位…)に従って、各参加者の順位を決定する処理のメインです。

  • ans = [0 for _ in range(n)]:
    結果を「元の入力順」で格納するためのリストを、一旦0で初期化して用意します
  • cur = persons[0][1]
    curは、現在の最上位のスコアです。ソート後のリストの先頭(最高得点)で初期化することで、一番高いスコアに初期化されます
  • num = 0
    現在の順位(num)を0(=1位)で初期化します
  • for i in range(n)
    ソート済みの persons リストを上から(高得点者から)順番に見るループです
    • if cur != persons[i][1]
      もし、前の人のスコア(cur)と今の人のスコア(persons[i][1])が違っていたら、 順位(num)を、現在のループインデックス i で更新します。 インデックスiは、順位(0インデックス)の値と同じなので、スコアが変わったら順位を合わせて更新する処理になります。
  • cur = persons[i][1]
    cur を今の人のスコアで更新し、次のループに備えます。
  • ans[persons[i][0]] = num
    persons[i][0] で「参加者の番号」を取り出し、ans リストの参加者の位置に、決定した順位 num を格納します。

これで、ansには各参加者の順位(0位から〜)が格納されます。

出力処理

for e in ans :
    if e == 0 :
        print("GOLD")
    elif e == 1 :
        print("SILVER")
    elif e == 2 :
        print("BRONZE")
    else :
        print("NONE")

順位が「元の入力順」で格納された ans リストを、先頭から順に見ていき、0ならGOLD, 1ならSILVER, 2ならBRONZE, それ以外の場合はNONEを表示します。

以上がプログラムの処理になります。

ルール2の解法

ルール2の解答は以下のようになります。

score = list(map(int, input().split()))

n = len(score)

unique = sorted(set(score), reverse=True)

rank = {s:i for i, s in enumerate(unique)}


for s in score :
    if rank[s] == 0 :
        print("GOLD")
    elif rank[s] == 1 :
        print("SILVER")
    elif rank[s] == 2 :
        print("BRONZE")
    else :
        print("NONE")

入力処理

標準入力から、データの読み込みを行います(ここは、ルール1と同じです)。

score = list(map(int, input().split()))

n = len(score)
  • n=len(score)で、参加者の人数を計算しています
  • input().split()で、スペース区切りされたスコア(20 30 40 ...)を読み込んでリスト['20', '30', '40', ...]します。※この時点では文字列です
  • map(int, ...)で、文字列リストの要素全てを整数に変換します
  • list(...)で、それを整数リストとしてscoreに格納します

順位づけテーブル作成

unique = sorted(set(score), reverse=True)

rank = {s:i for i, s in enumerate(unique)}

どのスコアが何位になるかを高速で調べるための対応表「辞書(dict)」を作成します。

  • set(score)
    score リストから重複をなくし、ユニークな(固有の)スコアだけの集合を作ります(例: {20, 30, 90, 100})。Pythonの場合は、setに変換することで重複を削除できます
  • sorted(..., reverse=True):
    作成した重複なしのスコアを reverse=True(降順、つまり点数が高い順)に並べ替えたリスト unique を作ります(例: [100, 90, 30, 20]
  • rank = {s:i ...}
    unique リストを元に、スコアをキー、順位(インデックス)をバリューとする辞書 rank を作成します。
    • enumerate(unique) は、リストの要素を (インデックス, 値) のペアで取り出します。(例: (0, 100), (1, 90), (2, 30), …)
    • {s:i ...} は、値 s(スコア)をキー、インデックス i(順位)をバリューとする辞書を作ります。
    • 結果: rank{100: 0, 90: 1, 30: 2, 20: 3} となります。
      これで「100点は0位 (金)」「90点は1位 (銀)」「30点は2位 (銅)」という対応表が完成しました。

出力処理

for s in score :
    if rank[s] == 0 :
        print("GOLD")
    elif rank[s] == 1 :
        print("SILVER")
    elif rank[s] == 2 :
        print("BRONZE")
    else :
        print("NONE")

各参加者のスコア(score)が辞書rankで何番目かをチェックし、0ならGOLD, 1ならSILVER, 2ならBRONZE, それ以外の場合はNONEを表示します。

以上が、プログラムの内容になります。

ルール1とルール2の違い

ルール1は上位の順位の人の人数、ルール2はスコアの1〜3位でメダルがもらえるという違いでしたが、プログラムはかなり違うことがわかります。とりあえず、順位を出力するプログラムの場合は、この2パターンを押さえておけば、大抵の場合に対応できるのではと思います。

まとめ

この記事では、順位に対してメダルを付与する処理を、Pythonで記述する方法について、代表的な2つのルールについてサンプルプログラムを提示して解説しました。競技プログラミングなどだけではなく、結構使う場合がありますので、作り方を覚えておくと良いです。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました