【Python】抽選プログラムつくってみた

はじめに

希望のコースが第3希望まで書かれたエクセルのリスト数百人分。手作業で振り分けていたのですが、数時間かかってしまうことに気づいてしまいました。気づいた自分天才か!と思いました。

めんどくさいことはプログラムにやらせよう

さすがに単純作業を数時間繰り返すのはゲームだけで十分です。プログラムに任せましょう。今再勉強中のPythonで試行錯誤しながらやってみました。ある程度動くことがわかったので備忘録として記事にしたいと思います。

概要

選択コース:A〜Dコース
生徒:A〜Dコースのうち3つを第1希望から第3希望まで選択する

データ構造

・選択コース

  1. コースナンバー
  2. コース名
  3. 定員数
  4. 応募者
  5. 決定生徒

・生徒

  1. 生徒ナンバー
  2. 生徒名
  3. 希望コース
  4. 決定コース

テストデータ

test.csv
生徒ナンバー,生徒名,第1希望コース,第2希望コース,第3希望コース,

1,student1,2,3,1
2,student2,4,1,3
3,student3,4,3,2
4,student4,3,2,4
5,student5,2,3,1
6,student6,1,3,4
7,student7,4,1,3
8,student8,4,2,3
9,student9,3,2,4
10,student10,3,1,2

プログラム

#!/usr/bin/env python

import csv
import random

class course:

  def __init__(self,no,name,limit):
    self.no = no
    self.name = name
    self.limit = limit
    self.applicant = []
    self.student_ = []

class student:

  def __init__(self,no,name,*choise):
    self.no = no
    self.name = name
    self.choise = choise
    self.decision_ = []

def clear_applicant(*ca):
  for c in ca:
    c.applicant.clear()

def return_decision_count(ast) -> int:
  #最小決定数を抽出
  decision_count = 9999999
  for s in  ast:
    if decision_count > len(s.decision_):
      decision_count = len(s.decision_)
  return decision_count

def choise_course(st,hope,decision,*ca):
  for c in ca:
    if c.no == st.choise[hope] and len(st.decision_) <= decision:
      c.applicant.append(st)

def decision_course(ast,dc):
  if dc.applicant:
    if len(dc.student_) <= dc.limit:
      if len(dc.applicant) >= dc.limit - len(dc.student_):
        tmp = dc.limit - len(dc.student_)
      else:
        tmp = len(dc.applicant)

      random.seed()
      tmpstudent_ = random.sample(dc.applicant,tmp)
      for c in tmpstudent_:
        c.decision_.append(dc.no)
        dc.student_.append(c)

def print_cource_student(print_cource):
  print(f'{print_cource.name}_student:{len(print_cource.student_)}')
  for c in print_cource.student_:
    print(f'No:{c.no},Name:{c.name},decision:{c.decision_}')

def main():
  #courseの定義
  course_A = course('1','Aコース',10)
  course_B = course('2','Bコース',10)
  course_C = course('3','Cコース',10)
  course_D = course('4','Dコース',10)

  #全ての生徒を格納
  AllStudent = []

  #CSVファイルからデータを読み込み
  with open('test.csv',newline='') as f:
    reader = csv.reader(f)

    for row in reader:
      AllStudent.append(student(row[0],row[1],row[2],row[3],row[4]))

  #希望者をコースごとに格納
  for i in range(3):
    clear_applicant(course_A,course_B,course_C,course_D)
    for st in AllStudent:
      choise_course(st,i,return_decision_count(AllStudent),
                    course_A,course_B,course_C,course_D)

    #抽選と確定
    decision_course(AllStudent,course_A)
    decision_course(AllStudent,course_B)
    decision_course(AllStudent,course_C)
    decision_course(AllStudent,course_D)
  
  for a in AllStudent:
    print(f'No:{a.no},Name:{a.name},decision:{a.decision_}')

  print_cource_student(course_A)
  print_cource_student(course_B)
  print_cource_student(course_C)
  print_cource_student(course_D)

if __name__ == '__main__':
  main()

プログラムの説明

コースの構造

class course:

  def __init__(self,no,name,limit):
    self.no = no
    self.name = name
    self.limit = limit
    self.applicant = []
    self.student_ = []

生徒の構造

class student:

def __init__(self,no,name,*choise):
self.no = no
self.name = name
self.choise = choise
self.decision_ = []

まずmain()でコースの定義をしてます。ここではコースNo,コース名、定員数を定義してます

 #courseの定義
  course_A = course('1','Aコース',10)
  course_B = course('2','Bコース',10)
  course_C = course('3','Cコース',10)
  course_D = course('4','Dコース',10)

続いてtest.csvから生徒データをAllStudent格納します。テストデータは作成がめちゃくちゃ面倒だったので別途テストデータ作成用のプログラムを作りました。気が乗れば紹介します。

  #全ての生徒を格納
  AllStudent = []

  #CSVファイルからデータを読み込み
  with open('test.csv',newline='') as f:
    reader = csv.reader(f)

    for row in reader:
      AllStudent.append(student(row[0],row[1],row[2],row[3],row[4]))

データ格納後、第1希望から順に抽選していきます。

 #希望者をコースごとに格納
  for i in range(3):
    clear_applicant(course_A,course_B,course_C,course_D)
    for st in AllStudent:
      choise_course(st,i,return_decision_count(AllStudent),
                    course_A,course_B,course_C,course_D)

    #抽選と確定
    decision_course(AllStudent,course_A)
    decision_course(AllStudent,course_B)
    decision_course(AllStudent,course_C)
    decision_course(AllStudent,course_D)

clear_applicantで希望ごとの応募者を毎回リセットします。引数で*変数名とすると明示しなくてもいくつも渡せるようになるので非常に快適。これでコース数が増えても対応できます。

def clear_applicant(*ca):
  for c in ca:
    c.applicant.clear()

choise_courseに生徒情報を渡し、希望コースごとに応募させます。ここではコース決定数が最小の生徒を優先させます。

def choise_course(st,hope,decision,*ca):
  for c in ca:
    if c.no == st.choise[hope] and len(st.decision_) <= decision:
      c.applicant.append(st)

コース決定数の最小を取得する関数はこちら。これで決まっているコースが少ない人が優先となります。

def return_decision_count(ast) -> int:
  #最小決定数を抽出
  decision_count = 9999999
  for s in  ast:
    if decision_count > len(s.decision_):
      decision_count = len(s.decision_)
  return decision_count

コースごとの応募者が確定したら抽選と決定を行います。

まず応募者がいることを確認し、定員数を超えていないかのチェック。また応募者数が定員数未満でないかを確認します。その後、応募者の中からrandom.sampleを使って抽選。確定したらコースの決定生徒と生徒の決定コースに情報を追記します。

def decision_course(ast,dc):
  if dc.applicant:
    if len(dc.student_) <= dc.limit:
      if len(dc.applicant) >= dc.limit - len(dc.student_):
        tmp = dc.limit - len(dc.student_)
      else:
        tmp = len(dc.applicant)

      random.seed()
      tmpstudent_ = random.sample(dc.applicant,tmp)
      for c in tmpstudent_:
        c.decision_.append(dc.no)
        dc.student_.append(c)

最後に確定情報を表示してますが、これはプリントではなくファイル等に出力する方が現実解かもしれません。

最後に

変数名とか関数名とか思いつきで適当にしてたのでおかしな部分はあるかと思いますが、これで数時間分の作業が一瞬で終わるようになったはずです。優先順位やコース数等をいじれば色々なところで活躍してくれると思ってます。あと使う人は自己責任でお願いします;

Python 1年生 体験してわかる!会話でまなべる!プログラミングのしくみ
Amazon.co.jp: Python 1年生 体験してわかる!会話でまなべる!プログラミングのしくみ eBook : 森 巧尚: Kindleストア

コメント