티스토리 뷰

python lecture/project

[python] 남/여 균일 매칭

burningrizen 2020. 5. 25. 22:33

1. 남여를 매칭하는 프로그램을 만든다.

 

2. 이전에 매칭된 이성은 매칭하지 않는다.

 

3. 한번에 매칭되는 할당양을 지정한다.

 

4. 성비가 적은쪽은 할당양 보다 더 많이 받는다.

> 예: 남=10, 여=5 일때 할당양이 2면 남자는 2, 여자는 4 정도를 받는다.

 

5. 어떤 사람이 많이 매칭되고 어떤사람이 적게 매칭되지 않게 균일하게 매칭한다. 

 

 

 

 

 

 

 

 

 

import random


class User:
    def __init__(self, _id=None, gender=None, history_users_group=None):
        self.id = _id
        self.gender = gender
        self.history_users_group = history_users_group if history_users_group else list()

    def group_to_users(self):
        total_history_users = []
        for _, history_users in self.history_users_group:
            total_history_users += history_users
        return total_history_users

    def check_overlap_user(self):
        total_history_users = self.group_to_users()
        return len(total_history_users) == len(list(set(total_history_users)))

    def __check_same_gender(self, other):
        return self.gender is other.gender

    def count_wrong_genders(self, others):
        return len(list(filter(lambda x: self.__check_same_gender(x) and x.id in self.group_to_users(), others)))

    def __prepare(self, delivery_id):
        self.history_users_group += [[delivery_id, []]]

    def __receive(self, ids):
        self.history_users_group[-1][1] += ids

    def get_target_users(self, delivery_id):
        history_users_group = list(filter(lambda users: users[0] == delivery_id, self.history_users_group))
        return history_users_group[0][1] if history_users_group else None

    def __filter_opposite(self, others, delivery_id, quota):
        others = self.__filter_self(others)
        others = self.__filter_gender(others)
        others = self.__filter_new_faces(others)
        others = User.__get_evenly(others, delivery_id, quota)
        return others

    def __filter_self(self, others):
        return filter(lambda x: x.id != self.id, others)

    def __filter_gender(self, others):
        return filter(lambda x: x.gender != self.gender, others)

    def __filter_new_faces(self, others):
        return filter(lambda x: x.id not in self.group_to_users(), others)

    @classmethod
    def __get_evenly(cls, others, delivery_id, quota):
        others = list(others)
        random.shuffle(others)
        return sorted(others, key=lambda x: len(x.get_target_users(delivery_id)))[:quota]

    @classmethod
    def prepares_users(cls, users, delivery_id):
        [user.__prepare(delivery_id) for user in users]

    def distribute_opposite(self, others, delivery_id, quota):
        others = self.__filter_opposite(others, delivery_id, quota)
        [other.__receive([self.id]) for other in others]
        self.__receive(list(map(lambda x: x.id, others)))
        return len(others)

    def __str__(self):
        return f"{self.__dict__}"


class Generator:
    def __init__(self, users=None):
        self.users = users if users else list()

    def add(self, man, girl):
        genders = [True] * man + [False] * girl
        for gender in genders:
            self.users.append(User(_id=len(self.users), gender=gender))

    def __str__(self):
        return "\n".join(map(str, self.users))


class Allocation:
    def __init__(self):
        self.cnt_delivery = 0
        self.cnt_not_enough = 0
        self.total_quota = 0

    def loop_deliver(self, users, quota, repeat):
        self.cnt_not_enough = 0
        for _ in range(repeat):
            self.__delivery(users, quota)
        print(f"count_not_enough/total_quota {self.cnt_not_enough}/{self.total_quota}")

    def __delivery(self, users, quota):
        User.prepares_users(users, self.cnt_delivery)
        for user in users:
            cnt_distribute = user.distribute_opposite(users, self.cnt_delivery, quota)
            if cnt_distribute == 0:
                self.cnt_not_enough += 1
            self.total_quota += cnt_distribute
        self.cnt_delivery += 1

    @classmethod
    def is_validation(cls, users):
        cnt_gender_errors = 0
        cnt_overlap_user_errors = 0
        for user in users:
            if user.check_overlap_user():
                cnt_gender_errors += user.count_wrong_genders(users)
            else:
                cnt_overlap_user_errors += 1
        print(f"cnt_gender_errors={cnt_gender_errors} cnt_overlap_user_errors={cnt_overlap_user_errors}")


if __name__ == '__main__':
    g = Generator()
    a = Allocation()
    g.add(100, 50)
    a.loop_deliver(g.users, quota=3, repeat=2)
    print(g)
    Allocation.is_validation(g.users)

'python lecture > project' 카테고리의 다른 글

[python] 시간변환  (0) 2020.05.27
[python] 스테가노그래피  (0) 2020.05.25
[python] # 연산 하기  (0) 2020.05.11
[python] 원소기호 맞추기  (0) 2020.05.09
[python] 주문시스템  (0) 2020.05.09
댓글