并行与分布式系统中的并行图匹配:基于消息传递的并行稳定匹配(Gale-Shapley)算法
字数 3763 2025-12-15 16:02:24

并行与分布式系统中的并行图匹配:基于消息传递的并行稳定匹配(Gale-Shapley)算法

题目描述
稳定匹配问题(Stable Matching Problem)是组合优化中的一个经典问题。给定两组规模均为N的参与者(例如,N位男士和N位女士),每位参与者对另一组的所有成员都有一个严格的偏好排名。目标是找到一个“完美匹配”(即一组N对不重复的配对),使得不存在这样一对未配对的男女,他们相比当前匹配的伴侣,都更偏好对方。这样的配对被称为“阻塞对”(blocking pair)。一个不存在任何阻塞对的完美匹配称为“稳定匹配”。Gale-Shapley算法(也称为延迟接受算法)可以在O(N²)时间内找到一个稳定匹配。我们的目标是在并行与分布式环境中,设计一个高效并行的Gale-Shapley算法。我们尤其关注一个消息传递(MPI-like)的并行化模型,其中计算节点通过通信网络交换“求婚”和“拒绝”消息,以协同计算稳定匹配。

解题过程循序渐进讲解

首先,我们回顾一下串行的Gale-Shapley算法。它采用“男士求婚,女士决定”的框架。算法维护每个男士的“求婚列表”(即他尚未求婚的、按偏好降序排列的女士列表),以及每位女士的“当前订婚对象”。算法循环执行,直到所有男士都订婚:

  1. 每一位尚未订婚的男士,向他偏好列表中排名最高且尚未求婚过的女士“求婚”。
  2. 当一位女士收到求婚时,她会在“当前求婚者”(可能是空)和“新求婚者”之间,根据她自己的偏好列表,选择她更偏好的一位作为“临时未婚夫”,并拒绝另一位(或如果之前没有订婚,则直接接受新求婚者)。
  3. 被拒绝的男士将这位女士从他的偏好列表中移除。
    当所有男士都订婚(即所有女士也恰好都有一位未婚夫)时,算法结束,得到的订婚关系就是最终的一个稳定匹配。

步骤1:问题并行化分析
串行算法的核心是一个顺序的“求婚-决策”循环。直接并行化的难点在于循环内存在数据依赖:一位女士在同一时间可能收到多个求婚,她的决策依赖于这些求婚的顺序(但最终结果与顺序无关,即Gale-Shapley算法具有“并发求婚”下的一致性性质)。这为我们提供了并行化的机会。我们可以允许多位男士同时向他们最偏好的女士发送求婚消息,女士们在收集到一批求婚消息后,可以并行地做出决定。因此,一个自然的并行策略是:

  • 将男士集合划分给多个处理器(进程)。
  • 每个处理器负责其分配到的男士的求婚动作,并向对应的女士所在处理器发送求婚消息。
  • 女士们同样被分配到各个处理器上,每个处理器负责处理发送到其管理的女士的求婚消息,并做出接受/拒绝的决策,然后发送回复消息。
  • 进程间通过消息传递进行通信。

步骤2:数据分布与初始化
假设我们有P个处理器(进程)。我们需要将N位男士和N位女士分配到P个处理器上。一种简单而有效的负载均衡方法是“循环分布”(cyclic distribution)或“块分布”(block distribution)。例如,采用块分布:

  • 将男士ID 0 到 N-1 连续地分成P块,第i个处理器(i从0到P-1)获得大小为 floor(N/P) 或 ceil(N/P) 的一块男士。
  • 同样,女士也按照同样的方式分块分布。注意:为了使得每位女士的决策能在本地进行,我们需要确保每位女士和她的偏好列表数据,存储在同一个处理器上。也就是说,处理器i存储了分配给它的那些女士的完整偏好列表(即对所有男士的偏好排名)。同时,为了方便男士求婚,每个处理器也需要存储它所管理的男士的偏好列表(即对所有女士的偏好排名)。

初始化时,每个处理器读取分配给它的男士和女士的偏好列表数据。每个处理器为其管理的每位男士维护一个“下一个求婚目标”的索引(初始化为0,即偏好列表中的第一位女士)。同时,为每位管理的女士维护一个“当前订婚对象”变量,初始为空。

步骤3:并行算法主循环
算法主循环在逻辑上仍然是迭代的,直到所有男士都订婚为止。在每一轮迭代中,包含以下阶段:

阶段A:男士求婚(发送消息)
每个处理器遍历其管理的所有尚未订婚的男士。对于每位这样的男士,查看他的“下一个求婚目标”索引指向哪位女士,然后向管理该女士的处理器发送一条求婚消息。求婚消息通常包含:求婚男士的ID。
求婚之后,无论是否会被接受,这位男士的“下一个求婚目标”索引递增(指向偏好列表中的下一位女士),以便为下一轮可能的求婚做准备(但注意,如果本轮他被接受,那么他在后续轮次中将保持订婚状态,不再求婚)。

由于女士分布在不同的处理器上,处理器需要知道每位女士位于哪个处理器。这可以通过一个简单的分布函数计算,例如 owner_of_woman(woman_id) = woman_id % P(如果使用循环分布)或 woman_id / block_size(如果使用块分布)。每个处理器维护一个“发送缓冲区”,为每个其他处理器累积要发送的求婚消息。

阶段B:消息交换与收集
所有处理器进行全局的同步通信(例如MPI_Alltoallv),将求婚消息发送到目标处理器。每个处理器在发送完自己的消息后,也会从其他处理器接收发送给它的求婚消息(即发送给它所管理的女士的求婚消息)。

阶段C:女士决策(本地处理)
每个处理器独立处理它收到的所有求婚消息。对于它管理的每一位女士,可能收到0个、1个或多个求婚消息。对于每位女士:

  1. 将她当前的订婚对象(如果有)视为一个“候选”。
  2. 将本轮所有向她求婚的男士作为“候选集合”。
  3. 根据该女士的偏好列表(存储在本处理器),从这些候选(包括当前的订婚对象)中选择她最偏好的一位作为新的“临时未婚夫”。
  4. 如果新的未婚夫不同于旧的订婚对象,则更新本地记录。如果存在旧的订婚对象,则意味着旧未婚夫被拒绝了。
  5. 对于所有未被选中的求婚者(包括被替换的旧未婚夫,如果有),他们都是被拒绝的。处理器需要记录这些拒绝,以便通知相应的男士。

阶段D:发送决策回复
处理器为每个被拒绝的男士生成一条拒绝消息,发送给管理该男士的处理器。同样,如果一位女士接受了新的求婚者(且不同于旧的),也需要向旧的未婚夫(如果有)发送拒绝消息。接受消息通常不需要显式发送,因为男士在下一轮发现自己仍处于订婚状态(即没有收到拒绝)即表示被接受。但为了明确状态变化,也可以发送接受消息。为了简化,常见的做法是:只发送拒绝消息。未被拒绝的求婚者即视为被接受。每个处理器再次通过同步通信(例如MPI_Alltoallv)将拒绝消息发送出去。

阶段E:处理回复并更新状态
每个处理器接收发送给它的拒绝消息。对于它管理的每位男士,如果收到关于某位女士的拒绝消息,则意味着他向该女士的求婚被拒绝。算法不需要男士立即采取行动,因为“下一个求婚目标”已经在阶段A递增了。然而,关键的更新是:如果一位男士当前处于订婚状态,但收到了来自他当前未婚妻的拒绝消息,那么他必须转为自由状态(即解除订婚)。因此,处理器需要根据拒绝消息更新其管理的男士的订婚状态:如果拒绝消息来自该男士的当前未婚妻,则将该男士的订婚状态清除(设为自由)。

步骤4:全局终止检测
在每轮迭代结束时,需要检查是否所有男士都已订婚(即稳定匹配已找到)。这需要一个全局的归约操作(例如MPI_Allreduce)。每个处理器计算其管理的男士中仍处于自由状态(即未订婚)的人数,然后通过全局求和,如果总和为0,则所有处理器退出算法;否则,进入下一轮迭代。

步骤5:复杂度与优化分析

  • 时间复杂度:在最坏情况下,串行Gale-Shapley算法最多进行N²次求婚(每位男士最多向N位女士求婚)。在并行版本中,理想情况下,每轮可以并行处理多个求婚。然而,由于女士决策的本地性和消息通信开销,并行轮数可能仍然是O(N)量级(如果每轮只有少量求婚被处理)。优化关键在于让每轮处理尽可能多的求婚,即让更多男士同时参与。但要注意,如果所有自由男士在同一轮都向同一位女士求婚,那么该女士的决策就成为瓶颈。但由于偏好列表的随机性,求婚目标通常是分散的,因此负载相对均衡。
  • 通信开销:每轮需要进行两次全局的All-to-all个性化通信(求婚和拒绝),每次通信的消息总数最多为自由男士的数量。通信开销可能成为性能瓶颈,特别是当进程数P很大时。优化方法包括:使用非阻塞通信重叠计算和通信;对消息进行聚合以减少通信次数;或者采用异步的、事件驱动的执行模型,其中进程在收到求婚消息后立即处理并回复,而不等待全局同步,但这会使得算法更复杂,且需要处理消息的乱序到达。

总结
基于消息传递的并行稳定匹配算法,通过将参与者和计算分布到多个处理器,并在每轮迭代中并行执行“男士求婚”、“消息交换”、“女士决策”、“回复通知”和“状态更新”等步骤,有效地加速了Gale-Shapley算法的执行。算法的正确性依赖于Gale-Shapley算法本身的特性——即无论求婚的顺序如何,只要男士按照偏好列表顺序求婚,女士总是选择当前可得到的最优者,算法最终都会收敛到同一个男士最优的稳定匹配。并行版本通过同步轮次来模拟这个顺序过程,同时利用并行性加速了每轮内的多对求婚-决策过程。

并行与分布式系统中的并行图匹配:基于消息传递的并行稳定匹配(Gale-Shapley)算法 题目描述 : 稳定匹配问题(Stable Matching Problem)是组合优化中的一个经典问题。给定两组规模均为N的参与者(例如,N位男士和N位女士),每位参与者对另一组的所有成员都有一个严格的偏好排名。目标是找到一个“完美匹配”(即一组N对不重复的配对),使得不存在这样一对未配对的男女,他们相比当前匹配的伴侣,都更偏好对方。这样的配对被称为“阻塞对”(blocking pair)。一个不存在任何阻塞对的完美匹配称为“稳定匹配”。Gale-Shapley算法(也称为延迟接受算法)可以在O(N²)时间内找到一个稳定匹配。我们的目标是在并行与分布式环境中,设计一个高效并行的Gale-Shapley算法。我们尤其关注一个消息传递(MPI-like)的并行化模型,其中计算节点通过通信网络交换“求婚”和“拒绝”消息,以协同计算稳定匹配。 解题过程循序渐进讲解 : 首先,我们回顾一下串行的Gale-Shapley算法。它采用“男士求婚,女士决定”的框架。算法维护每个男士的“求婚列表”(即他尚未求婚的、按偏好降序排列的女士列表),以及每位女士的“当前订婚对象”。算法循环执行,直到所有男士都订婚: 每一位尚未订婚的男士,向他偏好列表中排名最高且尚未求婚过的女士“求婚”。 当一位女士收到求婚时,她会在“当前求婚者”(可能是空)和“新求婚者”之间,根据她自己的偏好列表,选择她更偏好的一位作为“临时未婚夫”,并拒绝另一位(或如果之前没有订婚,则直接接受新求婚者)。 被拒绝的男士将这位女士从他的偏好列表中移除。 当所有男士都订婚(即所有女士也恰好都有一位未婚夫)时,算法结束,得到的订婚关系就是最终的一个稳定匹配。 步骤1:问题并行化分析 串行算法的核心是一个顺序的“求婚-决策”循环。直接并行化的难点在于循环内存在数据依赖:一位女士在同一时间可能收到多个求婚,她的决策依赖于这些求婚的顺序(但最终结果与顺序无关,即Gale-Shapley算法具有“并发求婚”下的一致性性质)。这为我们提供了并行化的机会。我们可以允许多位男士同时向他们最偏好的女士发送求婚消息,女士们在收集到一批求婚消息后,可以并行地做出决定。因此,一个自然的并行策略是: 将男士集合划分给多个处理器(进程)。 每个处理器负责其分配到的男士的求婚动作,并向对应的女士所在处理器发送求婚消息。 女士们同样被分配到各个处理器上,每个处理器负责处理发送到其管理的女士的求婚消息,并做出接受/拒绝的决策,然后发送回复消息。 进程间通过消息传递进行通信。 步骤2:数据分布与初始化 假设我们有P个处理器(进程)。我们需要将N位男士和N位女士分配到P个处理器上。一种简单而有效的负载均衡方法是“循环分布”(cyclic distribution)或“块分布”(block distribution)。例如,采用块分布: 将男士ID 0 到 N-1 连续地分成P块,第i个处理器(i从0到P-1)获得大小为 floor(N/P) 或 ceil(N/P) 的一块男士。 同样,女士也按照同样的方式分块分布。 注意 :为了使得每位女士的决策能在本地进行,我们需要确保每位女士和她的偏好列表数据,存储在同一个处理器上。也就是说,处理器i存储了分配给它的那些女士的完整偏好列表(即对所有男士的偏好排名)。同时,为了方便男士求婚,每个处理器也需要存储它所管理的男士的偏好列表(即对所有女士的偏好排名)。 初始化时,每个处理器读取分配给它的男士和女士的偏好列表数据。每个处理器为其管理的每位男士维护一个“下一个求婚目标”的索引(初始化为0,即偏好列表中的第一位女士)。同时,为每位管理的女士维护一个“当前订婚对象”变量,初始为空。 步骤3:并行算法主循环 算法主循环在逻辑上仍然是迭代的,直到所有男士都订婚为止。在每一轮迭代中,包含以下阶段: 阶段A:男士求婚(发送消息) 每个处理器遍历其管理的所有 尚未订婚 的男士。对于每位这样的男士,查看他的“下一个求婚目标”索引指向哪位女士,然后向管理该女士的处理器发送一条求婚消息。求婚消息通常包含:求婚男士的ID。 求婚之后,无论是否会被接受,这位男士的“下一个求婚目标”索引递增(指向偏好列表中的下一位女士),以便为下一轮可能的求婚做准备(但注意,如果本轮他被接受,那么他在后续轮次中将保持订婚状态,不再求婚)。 由于女士分布在不同的处理器上,处理器需要知道每位女士位于哪个处理器。这可以通过一个简单的分布函数计算,例如 owner_of_woman(woman_id) = woman_id % P (如果使用循环分布)或 woman_id / block_size (如果使用块分布)。每个处理器维护一个“发送缓冲区”,为每个其他处理器累积要发送的求婚消息。 阶段B:消息交换与收集 所有处理器进行全局的 同步通信 (例如MPI_ Alltoallv),将求婚消息发送到目标处理器。每个处理器在发送完自己的消息后,也会从其他处理器接收发送给它的求婚消息(即发送给它所管理的女士的求婚消息)。 阶段C:女士决策(本地处理) 每个处理器独立处理它收到的所有求婚消息。对于它管理的每一位女士,可能收到0个、1个或多个求婚消息。对于每位女士: 将她当前的订婚对象(如果有)视为一个“候选”。 将本轮所有向她求婚的男士作为“候选集合”。 根据该女士的偏好列表(存储在本处理器),从这些候选(包括当前的订婚对象)中选择她最偏好的一位作为新的“临时未婚夫”。 如果新的未婚夫不同于旧的订婚对象,则更新本地记录。如果存在旧的订婚对象,则意味着旧未婚夫被拒绝了。 对于所有未被选中的求婚者(包括被替换的旧未婚夫,如果有),他们都是被拒绝的。处理器需要记录这些拒绝,以便通知相应的男士。 阶段D:发送决策回复 处理器为每个被拒绝的男士生成一条拒绝消息,发送给管理该男士的处理器。同样,如果一位女士接受了新的求婚者(且不同于旧的),也需要向旧的未婚夫(如果有)发送拒绝消息。接受消息通常不需要显式发送,因为男士在下一轮发现自己仍处于订婚状态(即没有收到拒绝)即表示被接受。但为了明确状态变化,也可以发送接受消息。为了简化,常见的做法是: 只发送拒绝消息 。未被拒绝的求婚者即视为被接受。每个处理器再次通过 同步通信 (例如MPI_ Alltoallv)将拒绝消息发送出去。 阶段E:处理回复并更新状态 每个处理器接收发送给它的拒绝消息。对于它管理的每位男士,如果收到关于某位女士的拒绝消息,则意味着他向该女士的求婚被拒绝。算法不需要男士立即采取行动,因为“下一个求婚目标”已经在阶段A递增了。然而,关键的更新是: 如果一位男士当前处于订婚状态,但收到了来自他当前未婚妻的拒绝消息,那么他必须转为自由状态(即解除订婚) 。因此,处理器需要根据拒绝消息更新其管理的男士的订婚状态:如果拒绝消息来自该男士的当前未婚妻,则将该男士的订婚状态清除(设为自由)。 步骤4:全局终止检测 在每轮迭代结束时,需要检查是否所有男士都已订婚(即稳定匹配已找到)。这需要一个全局的归约操作(例如MPI_ Allreduce)。每个处理器计算其管理的男士中仍处于自由状态(即未订婚)的人数,然后通过全局求和,如果总和为0,则所有处理器退出算法;否则,进入下一轮迭代。 步骤5:复杂度与优化分析 时间复杂度 :在最坏情况下,串行Gale-Shapley算法最多进行N²次求婚(每位男士最多向N位女士求婚)。在并行版本中,理想情况下,每轮可以并行处理多个求婚。然而,由于女士决策的本地性和消息通信开销,并行轮数可能仍然是O(N)量级(如果每轮只有少量求婚被处理)。优化关键在于让每轮处理尽可能多的求婚,即让更多男士同时参与。但要注意,如果所有自由男士在同一轮都向同一位女士求婚,那么该女士的决策就成为瓶颈。但由于偏好列表的随机性,求婚目标通常是分散的,因此负载相对均衡。 通信开销 :每轮需要进行两次全局的All-to-all个性化通信(求婚和拒绝),每次通信的消息总数最多为自由男士的数量。通信开销可能成为性能瓶颈,特别是当进程数P很大时。优化方法包括:使用非阻塞通信重叠计算和通信;对消息进行聚合以减少通信次数;或者采用异步的、事件驱动的执行模型,其中进程在收到求婚消息后立即处理并回复,而不等待全局同步,但这会使得算法更复杂,且需要处理消息的乱序到达。 总结 : 基于消息传递的并行稳定匹配算法,通过将参与者和计算分布到多个处理器,并在每轮迭代中并行执行“男士求婚”、“消息交换”、“女士决策”、“回复通知”和“状态更新”等步骤,有效地加速了Gale-Shapley算法的执行。算法的正确性依赖于Gale-Shapley算法本身的特性——即无论求婚的顺序如何,只要男士按照偏好列表顺序求婚,女士总是选择当前可得到的最优者,算法最终都会收敛到同一个男士最优的稳定匹配。并行版本通过同步轮次来模拟这个顺序过程,同时利用并行性加速了每轮内的多对求婚-决策过程。