nkj23
@nkj23 (Yuta Nakamura)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

乱数をクラスのリストに組み込む方法

解決したいこと

乱数を使用して、'A','B','C'や整数を発生させ(重複を避ける)、それを組み込みたいです。
以下のソースコードはGMO Internet Group グループ研究開発本部さんの記事「【ノーベル賞×実務】pythonで簡単配属分け【ゲーム理論】」( https://recruit.gmo.jp/engineer/jisedai/blog/daalgorithm/ )から引用させていただいたものになります。
申し訳ないのですが、本当に知識がないので重複なしで乱数を発生させる方法や、文字列からランダムに抽出する方法などは調べてわかったのですが、それを組み込む方法がわかりません。
該当するソースコードののちに組み込みたい箇所を明記します。

該当するソースコード

class BrandNew:
 
    def __init__(self, preference:list):
        self.preference = preference
 
    def pop_preference(self):
        if len(self.preference) == 0: return None
 
        return self.preference.pop(0)
from functools import cmp_to_key
 
class Department:
 
    def __init__(self, preference: list, capacity:int):
        self.preference: list = preference
        self.capacity: int = capacity
        self.__this_step_applicants: list = [] # 今回ステップの応募者
        self.__keep_members: list = [] # 現在の仮決定者
 
    def _compare_bn(self, arg1, arg2):
        l_index = self.preference.index(arg1)
        r_index = self.preference.index(arg2)
 
        return l_index - r_index
 
    @property
    def keep_members(self):
        return self.__keep_members   
 
    @property
    def this_step_applicant(self):
        return self.__this_step_applicants
 
    @this_step_applicant.setter
    def this_step_applicant(self, value):
        # 新しい応募者を設定
        self.__this_step_applicant = value
        # 現在の仮決定者と応募者をあわせて、仮決定者を再選する
        self.__keep_members = sorted(self.__keep_members + self.__this_step_applicant, key=cmp_to_key(self._compare_bn))[:self.capacity]


brand_news = {
    1: BrandNew(['A', 'B', 'C']),
    2: BrandNew(['A', 'B', 'C']),
    3: BrandNew(['B', 'A', 'C']),
    4: BrandNew(['B', 'A', 'C']),
    5: BrandNew(['A', 'C', 'B']),
    6: BrandNew(['C', 'A', 'B']),
    7: BrandNew(['A', 'B', 'C']),
    8: BrandNew(['B', 'A', 'C']),
}
 
departments = {
    'A': Department([1, 2, 4, 8, 3, 6, 7, 5], 2),
    'B': Department([1, 7, 4, 5, 3, 2, 8, 6], 2),
    'C': Department([5, 6, 7, 8, 1, 2, 3, 4], 4),
}
 
def not_keeped_applicatns(brand_news, departments):
 
    ## 新人全員をリストアップ
    all_applicants = set(brand_news.keys())
 
    ## 仮決定している人間をリストアップ
    all_keeps = [x.keep_members for x in departments.values()]
    all_keeps = set(chain.from_iterable(all_keeps))
 
    ## 差分を取り、今回処理対象者を決定
    return all_applicants - all_keeps
 
# 全て配属されるまで実行
while len(xs := not_keeped_applicatns(brand_news, departments)) > 0:
    # 仮決定されてない応募者を処理対象者とする
    for x in xs:
        target_dpts = brand_news[x].pop_preference()
        departments[target_dpts].this_step_applicant = [x]
 
print(departments['A'].keep_members)
print(departments['B'].keep_members)
print(departments['C'].keep_members)

組み込みたい箇所

brand_news = {
    1: BrandNew(['A', 'B', 'C']),
    2: BrandNew(['A', 'B', 'C']),
    3: BrandNew(['B', 'A', 'C']),
    4: BrandNew(['B', 'A', 'C']),
    5: BrandNew(['A', 'C', 'B']),
    6: BrandNew(['C', 'A', 'B']),
    7: BrandNew(['A', 'B', 'C']),
    8: BrandNew(['B', 'A', 'C']),
}
 
departments = {
    'A': Department([1, 2, 4, 8, 3, 6, 7, 5], 2),
    'B': Department([1, 7, 4, 5, 3, 2, 8, 6], 2),
    'C': Department([5, 6, 7, 8, 1, 2, 3, 4], 4),
}

上記のBrandNew中の'A','B','C'やDepartment中の1~8の整数を重複を避けてランダムに生成したいです。
クラスのインスタンス?というものなのでしょうか。非常に無知で投げやりな質問になってしまっているのですが、どうかご容赦していただけるとありがたいです。これから勉強させていただきます。

また、質問の内容が不十分でしたらご指摘いただけると幸いです。

よろしくお願いいたします。

0

2Answer

random.shuffle()random.samle()を利用して,任意のリストから値をランダムに抽出することができます.

重複無し3件抽出
import random
print(random.sample(['A', 'B', 'C'], 3))

これを利用して,

brand_news = {
    1: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    2: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    3: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    4: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    5: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    6: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    7: BrandNew(random.sample(['A', 'B', 'C'], 3)),
    8: BrandNew(random.sample(['A', 'B', 'C'], 3)),
}
 
departments = {
    'A': Department(random.sample([i for i in range(1, 9)], 8), 2),
    'B': Department(random.sample([i for i in range(1, 9)], 8), 2),
    'C': Department(random.sample([i for i in range(1, 9)], 8), 4),
}

のように書くことができます.さらに,インスタンスごとに別の配列の並びである必要がある場合は,重複してない配列が生成されるまでrandom.sample()を繰り返すことで実現できます.

import random

dup = set()
def gen_random(arr):
    while True:
        ret = random.sample(arr, len(arr)).copy()
        if not ','.join(ret) in dup:
            dup.add(','.join(ret))
            return ret

brand_news = {
    1: BrandNew(gen_random(['A', 'B', 'C'])),
    2: BrandNew(gen_random(['A', 'B', 'C'])),
    3: BrandNew(gen_random(['A', 'B', 'C'])),
    4: BrandNew(gen_random(['A', 'B', 'C'])),
    5: BrandNew(gen_random(['A', 'B', 'C'])),
    6: BrandNew(gen_random(['A', 'B', 'C'])),
    #7: BrandNew(gen_random(['A', 'B', 'C'])),
    #8: BrandNew(gen_random(['A', 'B', 'C'])),
}

ただし,今回で言えばA, B, Cの3つしかないので,$3! = 6$パターンしか生成できないことに注意してください.7パターン目を生成しようとすると,無限ループに入ります.

3Like

Comments

  1. @nkj23

    Questioner

    ありがとうございます。
    random.sample()で実現することができました。迅速な回答本当に助かります。
  2. 解決されてよかったです.質問の方を「クローズ」にしていただけると終了になります.お疲れ様でした.

重複なしで乱数を発生させる

 絶対に無理です。乱数はバラバラであり、重複を含む概念です。例えば、サイコロは7回目は何がでるのでしょうか?7回目に7がでたら、それは、イカサマです。
 実際にサイコロを6回目振れば重複する目が出るはずです。

さて、

 便宜上、擬似的なユニークな乱数(重複を許容するなら)は簡単に生成できます。関数もあります。

 サイコロは6回しか振れないとします。
つまり、1から6の値がユニークに6回登場し、絶対に重複した値が登場しない仕組みです。そして、絶対に7回目は振れないとします。

 しかし、これは、イカサマではありません仕様です。

0.345
0.400
0.400
0.466
0.356
0.234

の値をsortして

dice[6]=0.235
dice[1]=0.345
dice[5]=0.356
dice[2]=0.400
dice[3]=0.400
dice[4]=0.466

とできたら、1から6の値がユニークに6回登場しますよネ!

1Like

Comments

  1. @nkj23

    Questioner

    ありがとうございます。
    重複について説明不足な点がありました。
    一つの、BrandNew内でA,B,Cが重複しなければよいという意味で、同じような組み合わせが生成される重複は認めることができる設定でした。
    説明不足で申し訳ありません。ただ、とても勉強になりました。

Your answer might help someone💌