🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

716閲覧

リストの要素の重複が正しく判定されません

Alice.

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/02/27 06:04

編集2021/02/27 12:09

前提・実現したいこと

席の希望が反映されるような席替えのシステムを作っています。

席番号ごとにその席を希望する人(出席番号1〜6)のリストを用意しました。
その中から座る人をランダムで選んだ席番号順のリストを作り、更に重複がない場合だけ表示されるようにしたいです。(複数の席に同じ出席番号が割り当たらないようにしたい)

発生している問題・エラーメッセージ

実行してみると表示されたリストに普通に重複が含まれており、何回実行しても重複のないリストが得られません。

Python

1import random 2 3seat_1 = [1,2,4,5,6] 4seat_2 = [1,2,3,4,5,6] 5seat_3 = [1,2,5,6] 6seat_4 = [1,2,3] 7seat_5 = [1,3,4,5,6] 8seat_6 = [6] 9 10 11seats = [seat_1,seat_2,seat_3,seat_4,seat_5, 12 seat_6] 13 14 15 16#ランダムに座る人を選ぶ関数を定義 17def classroom(): 18 result = [] 19 for num in range(0,6): 20 random.choice(seats[num]) 21 result.append(random.choice(seats[num])) 22 return result 23 24 25#重複がない場合にTrue になる関数を定義 26def no_duplicates(seq): 27 return len(seq) == len(set(seq)) 28 29#重複がない場合だけリストを表示 30for i in range(100): 31 classroom() 32 if no_duplicates(classroom()) == True: 33 print(classroom())

試したこと

文字数len()と文字の種類数len(set())も表示させてみると2点問題がありました。
①文字の種類数が正しくない(正しい時もある)
②文字数と文字の種類数が一致していないのに「重複が無い」(no_duplicates()がTrue)と判断されている

本当に初心者なので、質問以外にもおかしな点などありましたら教えて頂けるとありがたいです。よろしくお願いします。

追記

can110さんのコードを使って下のように変更しました。

Python

1import random 2 3seat_1 = [2,3,4,5,6,7,8,12,13,15,19,22,30,31] 4seat_2 = [2,3,4,5,6,7,8,13,15,16,19,20,22,25,26,30,31,36] 5seat_3 = [2,4,6,7,8,14,15,16,20,25,26,30,33,36,37] 6seat_4 = [1,2,4,6,7,8,14,15,16,17,20,23,25,26,28,30,33,36,37] 7seat_5 = [1,2,4,6,7,8,14,16,17,20,23,25,26,28,30,33,36,37] 8seat_6 = [29] 9seat_7 = [2,3,6,7,8,10,15,16,19,22,25,30,31,36] 10seat_8 = [2,6,7,8,10,14,15,16,25,30,33,36,37] 11seat_9 = [1,2,6,7,8,10,14,15,16,17,23,25,28,30,33,36,37] 12seat_10 = [1,2,6,7,8,14,16,17,23,25,28,30,32,33,36,37] 13seat_11 = [1,2,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 14seat_12 = [5,6,7,8,12,13,22,30,31] 15seat_13 = [5,6,7,8,13,22,30,31] 16seat_14 = [6,7,8,10,16,22,30,31] 17seat_15 = [6,7,8,10,14,16,21,30,33,37] 18seat_16 = [1,6,7,8,10,14,16,17,21,23,28,30,33,37] 19seat_17 = [1,6,7,8,14,16,17,21,23,28,30,33,37] 20seat_18 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 21seat_19 = [5,6,7,8,12,13,22,30] 22seat_20 = [5,6,7,8,13,22,24,30] 23seat_21 = [6,7,8,10,16,22,24,30] 24seat_22 = [6,7,8,10,14,16,21,30,33,37] 25seat_23 = [1,6,7,8,10,14,16,17,21,23,28,30,33,37] 26seat_24 = [1,6,7,8,14,16,17,21,23,28,30,33,37] 27seat_25 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 28seat_26 = [5,6,7,8,12,13,22,30] 29seat_27 = [5,6,7,8,13,22,24,30] 30seat_28 = [6,7,8,10,16,22,24,30] 31seat_29 = [6,7,8,10,14,16,24,30,33,37] 32seat_30 = [1,6,7,8,10,14,16,17,23,28,30,33,37] 33seat_31 = [1,6,7,8,14,16,17,23,28,30,33,37] 34seat_32 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 35seat_33 = [4,5,6,7,8,13,22,24,27,30] 36seat_34 = [27] 37seat_35 = [11] 38seat_36 = [9] 39seat_37 = [35] 40 41 42seats = [seat_1,seat_2,seat_3,seat_4,seat_5, 43 seat_6,seat_7,seat_8,seat_9,seat_10, 44 seat_11,seat_12,seat_13,seat_14,seat_15, 45 seat_16,seat_17,seat_18,seat_19,seat_20, 46 seat_21,seat_22,seat_23,seat_24,seat_25, 47 seat_26,seat_27,seat_28,seat_29,seat_30, 48 seat_31,seat_32,seat_33,seat_34,seat_35, 49 seat_36,seat_37] 50 51# (席番号, 候補リスト)に整形 52seats = [(i+1,v) for i,v in enumerate(seats)] 53 54ret = [] 55while seats: 56 # 候補の少ない席から抽選する 57 i = seats.index(min(seats, key=lambda v:len(v[1]))) 58 seat = seats.pop(i) # popによって今から抽選する席番号は取り除かれる 59 60 # 抽選 61 person = random.choice(seat[1]) # 当選した人番号 62 63 # 結果を保存 64 ret.append((seat[0], person)) # (席番号, 人番号) 65 66 # 各席から当選した人を取り除く 67 seats = [ (seat[0], [p for p in seat[1] if p != person]) for seat in seats] 68 69ret.sort(key=lambda v:v[0]) # 結果を席番号順に 70print(ret)

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

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

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

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

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

guest

回答2

0

ベストアンサー

no_duplicates(~)で判定しているモノとprint(~)しているモノ、すなわちclassroomが異なるためです。
cr = classroom()と変数に代入して、それに対して判定、出力すればよいです。

Python

1#重複がない場合だけリストを表示 2for i in range(100): 3 cr = classroom() 4 if no_duplicates(cr): 5 print(cr) 6 7#[5, 4, 2, 1, 3, 6] 8#[5, 4, 2, 1, 3, 6] 9#[1, 5, 2, 3, 4, 6]

追記

いや、以下ではだめですね。
席1に1と3番、席2と席3に1と2番の人が希望した場合、席1に1番の人が座ると破綻しますね。

~~ちなみに、以下のような抽選方法だと席の数のループだけで結果が得られます。
なお、「席1と席2に6番の人だけが応募する」というような、結果が得られない組み合わせは考慮していません。
~~

Phthon

1import random 2 3seat_1 = [1,2,4,5,6] 4seat_2 = [1,2,3,4,5,6] 5seat_3 = [1,2,5,6] 6seat_4 = [1,2,3] 7seat_5 = [1,3,4,5,6] 8seat_6 = [6] 9 10seats = [seat_1,seat_2,seat_3,seat_4,seat_5, 11 seat_6] 12 13seats = [[1,3],[1,2],[1,2]] # だめなケースを追記 14 15# (席番号, 候補リスト)に整形 16seats = [(i+1,v) for i,v in enumerate(seats)] 17 18ret = [] 19while seats: 20 # 候補の少ない席から抽選する 21 i = seats.index(min(seats, key=lambda v:len(v[1]))) 22 seat = seats.pop(i) # popによって今から抽選する席番号は取り除かれる 23 24 # 抽選 25 person = random.choice(seat[1]) # 当選した人番号 26 27 # 結果を保存 28 ret.append((seat[0], person)) # (席番号, 人番号) 29 30 # 各席から当選した人を取り除く 31 seats = [ (seat[0], [p for p in seat[1] if p != person]) for seat in seats] 32 33ret.sort(key=lambda v:v[0]) # 結果を席番号順に 34print(ret)# [(1, 4), (2, 3), (3, 2), (4, 1), (5, 5), (6, 6)]

#追記2
処理を見直しました。
これならおそらくうまく割り振られると思います。

Python

1import random 2 3seat_1 = [2,3,4,5,6,7,8,12,13,15,19,22,30,31] 4seat_2 = [2,3,4,5,6,7,8,13,15,16,19,20,22,25,26,30,31,36] 5seat_3 = [2,4,6,7,8,14,15,16,20,25,26,30,33,36,37] 6seat_4 = [1,2,4,6,7,8,14,15,16,17,20,23,25,26,28,30,33,36,37] 7seat_5 = [1,2,4,6,7,8,14,16,17,20,23,25,26,28,30,33,36,37] 8seat_6 = [29] 9seat_7 = [2,3,6,7,8,10,15,16,19,22,25,30,31,36] 10seat_8 = [2,6,7,8,10,14,15,16,25,30,33,36,37] 11seat_9 = [1,2,6,7,8,10,14,15,16,17,23,25,28,30,33,36,37] 12seat_10 = [1,2,6,7,8,14,16,17,23,25,28,30,32,33,36,37] 13seat_11 = [1,2,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 14seat_12 = [5,6,7,8,12,13,22,30,31] 15seat_13 = [5,6,7,8,13,22,30,31] 16seat_14 = [6,7,8,10,16,22,30,31] 17seat_15 = [6,7,8,10,14,16,21,30,33,37] 18seat_16 = [1,6,7,8,10,14,16,17,21,23,28,30,33,37] 19seat_17 = [1,6,7,8,14,16,17,21,23,28,30,33,37] 20seat_18 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 21seat_19 = [5,6,7,8,12,13,22,30] 22seat_20 = [5,6,7,8,13,22,24,30] 23seat_21 = [6,7,8,10,16,22,24,30] 24seat_22 = [6,7,8,10,14,16,21,30,33,37] 25seat_23 = [1,6,7,8,10,14,16,17,21,23,28,30,33,37] 26seat_24 = [1,6,7,8,14,16,17,21,23,28,30,33,37] 27seat_25 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 28seat_26 = [5,6,7,8,12,13,22,30] 29seat_27 = [5,6,7,8,13,22,24,30] 30seat_28 = [6,7,8,10,16,22,24,30] 31seat_29 = [6,7,8,10,14,16,24,30,33,37] 32seat_30 = [1,6,7,8,10,14,16,17,23,28,30,33,37] 33seat_31 = [1,6,7,8,14,16,17,23,28,30,33,37] 34seat_32 = [1,4,6,7,8,14,16,17,18,23,28,30,32,33,34,37] 35seat_33 = [4,5,6,7,8,13,22,24,27,30] 36seat_34 = [27] 37seat_35 = [11] 38seat_36 = [9] 39seat_37 = [35] 40 41seats = [seat_1,seat_2,seat_3,seat_4,seat_5, 42 seat_6,seat_7,seat_8,seat_9,seat_10, 43 seat_11,seat_12,seat_13,seat_14,seat_15, 44 seat_16,seat_17,seat_18,seat_19,seat_20, 45 seat_21,seat_22,seat_23,seat_24,seat_25, 46 seat_26,seat_27,seat_28,seat_29,seat_30, 47 seat_31,seat_32,seat_33,seat_34,seat_35, 48 seat_36,seat_37] 49 50# (席番号, 候補リスト)に整形 51seats = [(i+1,v) for i,v in enumerate(seats)] 52 53ret = [] 54while seats: 55 for i, seat in enumerate(seats): 56 # 候補がいる席に対して抽選 57 # (最初は人気がなくて候補がいない席もあることを考慮) 58 if len(seat[1]) >= 1: 59 seat = seats.pop(i) # popによって今から抽選する席番号は取り除かれる 60 61 # 抽選して結果を保存 62 win = random.choice(seat[1]) # 当選した人番号 63 ret.append((seat[0], win)) # (席番号, 人番号) 64 65 # 各席から当選した人を取り除く 66 seats = [ (s[0], [p for p in s[1] if p != win]) for s in seats] 67 68 # 落選した人で、他にどの席も希望していない人は 69 # あぶれるので有無を言わせず(ランダムに)別の席の抽選に振り分ける 70 loses = [v for v in seat[1] if v != win] 71 for lose in loses: 72 is_exist = False 73 for seat in seats: 74 if lose in seat[1]: 75 is_exist = True 76 break 77 if not is_exist: 78 seat = random.choice(seats) 79 seat[1].append(lose) 80 break 81 82assert(len(ret) == len(set([v[0] for v in ret]))) # すべての席に割り当てられた 83assert(len(ret) == len(set([v[1] for v in ret]))) # すべての人が割り当てられた 84 85ret.sort(key=lambda v:v[0]) # 結果を席番号順に 86print(ret)

投稿2021/02/27 07:19

編集2021/02/27 15:06
can110

総合スコア38341

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

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

Alice.

2021/02/27 09:46

丁寧に解答して頂き、ありがとうございます!2つ目の抽選方法を使って、一番上の各席の希望者とseats = []の部分だけ数を増やしてみたところ(実際に自分のクラスで取った37人分の希望を使いました)、下のようなエラーが出ました。 IndexError Traceback (most recent call last) <ipython-input-262-56be16a87135> in <module> 59 60 # 抽選 ---> 61 person = random.choice(seat[1]) # 当選した出席番号 62 63 # 結果を保存 ~\anaconda3\lib\random.py in choice(self, seq) 288 i = self._randbelow(len(seq)) 289 except ValueError: --> 290 raise IndexError('Cannot choose from an empty sequence') from None 291 return seq[i] 292 IndexError: Cannot choose from an empty sequence 色々と調べましたが原因が掴めません。人数と希望を変更しただけなのですがどうすれば解決できるでしょうか。
can110

2021/02/27 11:17

実際のコードを質問本文に追記くださると検証できるかもしれません。 抽選の過程で、ある席において、応募した人がすべて他の席に割り当てられた結果、いなくなってしまった可能性があります。 回答に記載したように「席1と席2に6番の人だけが応募する」ようなケースになっていないでしょうか?
Alice.

2021/02/27 12:11

先程本文にコードを追記したので、見ていただけるとありがたいです。
can110

2021/02/27 13:13

あ、だめですね。 「seats = [[1,3],[1,2],[1,2]]」のケースで 席1に1番の人が座ると、3番の人は座れず、なおかつ席2と席3は2番の人しか候補がいなくなり破綻します。
Alice.

2021/02/28 05:32

返信が遅くなり申し訳ありません。2つ目の追記を実行してみたところ上手くいきました!早速学校で使ってみようと思います。初めてネットで質問をしたのですが、こんなに丁寧に回答くださる方がいることに感動してしまいました。本当にありがとうございます。
guest

0

関数は呼ばれるたびに結果を返します。

pyhon

1for i in range(100): 2 classroom() 3 if no_duplicates(classroom()) == True: 4 print(classroom())

で3回呼ばれているclassroom()は別々の値を返しているので、判定したときの値とプリントしたときの値が違うのが原因です。
変数に代入してそれを判定してプリントしましょう。

投稿2021/02/27 06:58

ppaul

総合スコア24670

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

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

Alice.

2021/02/28 05:32 編集

「関数は呼ばれるたびに結果を返す」と書いて頂いたのが分かりやすくて理解できました。迅速な回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問