前提・実現したいこと
Pythonに興味を持ち、先月からwebで調べたりしながら勉強しています。
全くの初心者です。
勉強用に出来るだけ公平な当番表を作ってみました。
(練習用なので無駄な部分が多いかと思います)
実現したいことは、
・人名を連続入力する (例:山田、田中、佐藤...)
・1回の当番あたりの人数を入力する (例:2人)
・53週分の当番表が出力される
・多少の誤差はあるものの、人名ごとに担当回数がほぼ同じになるような
(=出来るだけ公平な)当番表の完成
です。
公平性を保つ為、53週で必要な当番人数の数を上回るように
リストを複製し、複製したリストから人数分ランダムに人名を選び、
選んだ名前はリストから消す、を53回繰り返しています。
しかし、当番数を複数にすると(2人以上)、
どうしても重複してしまいます(例:山田、山田 ← 2人選ばれていない)
重複無しの当番が人数分出力されるように改善したく思っています。
発生している問題・エラーメッセージ
発生している問題 (1)1番大きな問題 重複無しで指定の当番人数分の人名を抽出したいが、重複してしまう。 (2)2番目以降の問題 ・もっとスマートに記述できるなら勉強までに教えていただきたく思っています。 ・Excelに出力する部分の記述がいけないのか['']がついた状態で、セルに入ってしまい、 エクセルの加工がしにくいので、出力時に人名だけがセルに入るように改善したいです。 ・もし可能なら最初に「当番表の年」を聞いて、現在「第--週」と出力されている部分を その週の「月曜日の日付」で出力したいと思っています。
該当のソースコード
#! python3 # Rota.py # 当番表生成 print('たぶん公平な当番表作成') print('●やっていること●') print('1.当番人数×53週 = 1年に必要な当番人数を算出') print('2.「必要な当番人数」を上回るように「メンバーリスト」を複製') print('3.メンバーリストからランダムにメンバーを選び、選ばれたメンバーはリストから消す') print('4.ランダムで、かつ回数に偏りが生まれにくい当番表の完成(たぶん)') print('5.さらに出来上がったリストをエクセルで出力(適当なのでなおしたい)') print('') #当番表の名前 print('当番表の名前は何にしますか? (例)掃除当番表') print('※ 出力されるエクセルファイルのファイル名になるので注意') rotaname = input() print('当番表の名前は「' + str(rotaname) + '」となります:') #メンバーの名前を聞く orgmembers = [] while True: print('メンバー' + str(len(orgmembers) + 1) + 'の名前を入力してください' + '(終了するにはEnterキーだけ押してください)') name = input() if name == '': break orgmembers = orgmembers + [name] print('メンバーは' + str(len(orgmembers)) + '名で、以下の通りですね:') for name in orgmembers: print(' '+name) #当番1回あたりの人数を聞く print('当番人数は一回当たり何人ですか?(半角の数字で入力)') rotanum = input() print('当番1回当たり' + str(rotanum) + '名ですね:') #当番表出力 print('第53週までの当番表を出力します') import random members = orgmembers #トータル何人が当番か計算 # トータルの人数×53週を計算し、totalmemに格納 totalmem = 53 * int(rotanum) #トータルの当番数を、リストの当番で割って商qと余りmodを算出 # totalmemをorgmembersで割って、その商を算出 q = int(totalmem) // len(orgmembers) mod = int(totalmem) % len(orgmembers) #トータルメンバーリストの作成 # qの回数+1だけ、orgmembersを繰り返しtmlistに追加 tmlist = orgmembers for i in range(int(q)): tmlist = tmlist + orgmembers #トータルメンバーリストのシャッフル # 念のため、tmlistをシャッフルする random.shuffle(tmlist) #当番人数分の名前を抽出 # 最終的なメンバーをfinalmemに格納 # エクセルファイルに最終的なメンバー表を出力 # memに、tmlist内からrotanumの数だけランダム抽出し、格納 # mem1に、memの固有要素数を抽出し、格納(重複が削除される) # memの長さとmem1の長さを比較し、等しくなかった場合はmemの選びなおし、等しかったら次へ # (例:memリスト(選んだメンバーリストの要素数)と、mem1(選んだメンバーリストの個有数)が一致しないと、 # 重複があるということになるので、重複がなくなるまで選びなおす) # memの長さがrotanumより小さかった場合、memの選びなおし # (例:2人当番で重複するとmem1が1となり、memと等しくなってしまい、 # 重複があるのに処理が次に移ってしまう為) # 重複無しの選抜メンバーにfinalmemに第何週かの情報をつけて結果を格納 # Excelに出力 # 53週分ループしたら終わり finalmem = [] totaldelmem = [] import openpyxl wb = openpyxl.Workbook() sheet = wb.active for i in range(53): mem = random.sample(tmlist,int(rotanum)) mem1 = list(set(mem)) if len(mem1) < len(mem): mem = random.sample(tmlist,int(rotanum)) mem1 = list(set(mem)) if len(mem1) != int(rotanum): mem = random.sample(tmlist,int(rotanum)) mem1 = list(set(mem)) sheet['A' + str(i+1)] = '第' + str(i+1) + '週 ' + str(mem) print('第' + str(i+1) + '週 ' + str(mem)) finalmem.append(str(mem)) for j in range(int(rotanum)): delmem = mem[0] tmlist.remove(delmem) mem.remove(delmem) totaldelmem.append(delmem) wb.save(str(rotaname) + '.xlsx') #finalmemに結果が格納されてるけど、うまく加工できない #後で何とかしたい #公平度チェック print('今回の公平度は以下の通り') uniq_tdm = list(set(totaldelmem)) for k in range(len(orgmembers)): tdm = uniq_tdm[0] print('●'+(tdm) + 'の当番回数は') print(totaldelmem.count(tdm)) uniq_tdm.remove(tdm) #おまけ print('エクセルファイルは.pyファイルと同じフォルダに出来てるよ') print('ファイル名は' + str(rotaname) + '.xlsx です')
試したこと
当初重複に対応するため、
memに、tmlist内からrotanumの数だけランダム抽出し、格納
mem1に、memの固有要素数を抽出し、格納(重複が削除される)
memの長さとmem1の長さを比較し、等しくなかった場合はmemの選びなおし、等しかったら次へ
(例:memリスト(選んだメンバーリストの要素数)と、mem1(選んだメンバーリストの個有数)が
一致しないと、重複があるということになるので、重複がなくなるまで選びなおす)
memの長さがrotanumより小さかった場合、memの選びなおし
(例:2人当番で重複するとmem1が1となり、memと等しくなってしまい、
重複があるのに処理が次に移ってしまう為)
という仕組みで回避を試みていますが、これでもやっぱり重複してしまいます。
補足情報(FW/ツールのバージョンなど)
Anaconda3 (64-bit)でPython 3.6.4 を使用しています

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/05/29 09:33
2018/05/29 09:35
2018/05/29 09:42
2018/05/29 10:31