Gale-Shapley algorithm 用c++实现

69 阅读2分钟
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>

// 定义性别
typedef enum {
  MALE = 0,
  FEMALE = 1,
} Sex;

// 定义婚姻状态
typedef enum {
  NOMARRIED,
  MARRIED,
} MarriedStatus;

// 定义参加配对的人员类型
struct Partner {
  std::string name;
  int age;
  Sex sex;
  MarriedStatus married_status;
  std::string married_name;
  std::vector<std::string> prefer_partner_name;

  Partner(std::string name, int age, Sex sex,
          std::vector<std::string> &prefer_partner_name)
      : name{name}, age{age}, sex{sex}, married_status{NOMARRIED},
        married_name{""}, prefer_partner_name{prefer_partner_name} {};

  ~Partner() = default;
};

// 配对会的管理者,负责登记参会人员,同时采用Gale-Shapley algorithm将男女配对
struct Manager {
  std::vector<Partner> male_partner_lists;
  std::vector<Partner> female_partner_lists;
  Manager() = default;

  // 登记信息,并将参会人员加入各自性别的管理表
  void logging_message(const std::string &filename) {
    // 格式为 filename文件的每一行为sex = 0/1 name age name1 name2 name3...
    // name为登记的人员名,name1,name2,是name偏好的信息列表
    std::ifstream file(filename);
    std::string line;
    if (!file.is_open()) {
      throw std::runtime_error("Can't open file\n");
    }
    while (getline(file, line)) {
      std::istringstream iss(line);
      std::string token;
      int count = 0;
      int age;
      Sex sex;
      std::string name;
      std::vector<std::string> prefer;
      while (iss >> token) {
        switch (count) {
        case 0:
          // BUG 转换失败暂未提供处理方法
          sex = static_cast<Sex>(std::stoi(token));
          break;
        case 1:
          name = token;
          break;
        case 2:
          age = std::stoi(token);
          break;
        default:
          prefer.push_back(token);
        }
        count++;
      }

      if (sex == MALE) {
        male_partner_lists.push_back(Partner(name, age, sex, prefer));
      } else if (sex == FEMALE) {
        female_partner_lists.push_back(Partner(name, age, sex, prefer));
      }
    }
    // 将名字与类的index映射
    fill_partner_map();
    file.close();
  };

  Partner &get_partner_by_name(std::string name) {
    auto index_male = male_partner_map.find(name);
    auto index_female = female_partner_map.find(name);
    if (index_male != male_partner_map.end()) {
      return male_partner_lists[index_male->second];
    } else if (index_female != female_partner_map.end()) {
      return female_partner_lists[index_female->second];
    } else {
      throw std::runtime_error("name isn't in!\n");
    }
  }

  void Gale_shapley_algorithm() {
    std::stack<Partner *, std::vector<Partner *>> unmaried_lists{};
    for (Partner &temp_male : male_partner_lists) {
      unmaried_lists.push(&temp_male);
    }

    while (!unmaried_lists.empty()) {
      Partner *temp_male_point = unmaried_lists.top();
      Partner &temp_male = *temp_male_point;
      for (std::string prefer_female_name : temp_male.prefer_partner_name) {
        Partner &temp_female = get_partner_by_name(prefer_female_name);

        if (temp_female.married_status == MARRIED) {
          int prev_married = find_index(temp_female.prefer_partner_name,
                                        temp_female.married_name);
          int prev_temp_male =
              find_index(temp_female.prefer_partner_name, temp_male.name);

          if (prev_married == -1 && prev_temp_male == -1) {
            throw std::runtime_error("优先级出问题");
          }
          if (prev_temp_male != -1 && prev_temp_male < prev_married) {
            Partner &temp_married =
                get_partner_by_name(temp_female.married_name);
            // 先放这里,避免之后被修改栈顶,导致错误
            unmaried_lists.pop();
            temp_married.married_name = "";
            temp_married.married_status = NOMARRIED;
            std::cout << temp_married.name << ":被别人拆散离婚" << std::endl;
            unmaried_lists.push(&temp_married);
            temp_female.married_name = temp_male.name;
            temp_male.married_status = MARRIED;
            temp_male.married_name = temp_female.name;
            // 放这里的逻辑就不对了,因为前面添加一个元素,改变了栈顶!
            //  unmaried_lists.pop();
            break;
          }
        } else {
          temp_male.married_name = temp_female.name;
          temp_male.married_status = MARRIED;
          temp_female.married_name = temp_male.name;
          temp_female.married_status = MARRIED;
          std::cout << temp_male.name << ":正常结婚" << std::endl;
          unmaried_lists.pop();
          break;
        }
      }
    }
  }

  void print_married_status() {
    for (Partner &temp_male : male_partner_lists) {

      std::cout << temp_male.name << ":" << temp_male.married_status << "->"
                << temp_male.married_name << std::endl;
    }
  }

  ~Manager() = default;

private:
  std::map<std::string, int> male_partner_map;
  std::map<std::string, int> female_partner_map;
  void fill_partner_map() {

    int i = 0;
    for (Partner &male_temp : male_partner_lists) {
      male_partner_map[male_temp.name] = i;
      i++;
    }
    i = 0;
    for (Partner &female_temp : female_partner_lists) {
      female_partner_map[female_temp.name] = i;
      i++;
    }
  }

  int find_index(std::vector<std::string> vector, std::string name) {
    auto it = std::find(vector.begin(), vector.end(), name);
    if (it != vector.end()) {
      return std::distance(vector.begin(), it);
    }
    return -1;
  }
};

int main() {
  Manager manger;
  manger.logging_message("./abc.txt");
  manger.Gale_shapley_algorithm();
  manger.print_married_status();
}

测试的abc.txt

0 male_a 20 female_a female_b female_c female_d
0 male_b 20 female_b female_a female_c female_d
0 male_c 20 female_b female_c female_d female_a
0 male_d 20 female_c female_d female_b female_a
1 female_a 20 male_a male_b male_d male_c
1 female_b 20 male_b male_c male_a male_d
1 female_c 20 male_d male_a male_c male_b
1 female_d 20 male_c male_b male_a male_d