You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在R中实现笔友配对:循环剔除已配对人员的方案求助

Hey Anna, nice work getting your compatibility scores and ranked match lists ready—you’re already halfway there! Let’s solve this pairing problem so everyone gets a pen pal from their top 8 picks, with no repeats and the best possible overall compatibility.

We’ve got two solid approaches here: a greedy priority-based method (easy to implement and aligns with your "most picky first" sorting) and a global optimal method (uses graph theory to maximize total compatibility).


1. Greedy Priority Method (Aligns with Your Picky-First Sort)

This method prioritizes the most selective users first (the ones you sorted by lowest best-match score), ensuring they get their top available pick before moving to less picky users.

Step-by-Step Implementation

First, let’s clean up your data into a usable long format:

library(tidyverse)

# Assume your raw data is stored in a data frame called `raw_match_data`
cleaned_data <- raw_match_data %>%
  # Rename columns for clarity
  rename(user_id = indexed_fake, best_score = apply.fin_fake..1..max.) %>%
  # Convert wide X1-X8 columns to long format (user, match rank, candidate ID)
  pivot_longer(cols = starts_with("X"), 
               names_to = "match_rank", 
               values_to = "candidate_id") %>%
  # Convert rank to integer (1-8)
  mutate(match_rank = as.integer(str_remove(match_rank, "X"))) %>%
  # Sort by your picky-first order (lowest best_score first)
  arrange(best_score, user_id, match_rank)

Now run the greedy pairing loop:

# Initialize a data frame to track paired users
paired_users <- tibble(user_id = integer(), paired_with = integer())

# Get the priority list of users (most picky first)
priority_list <- cleaned_data %>%
  distinct(user_id, best_score) %>%
  arrange(best_score) %>%
  pull(user_id)

# Loop through each user in priority order
for (current_user in priority_list) {
  # Skip if user is already paired
  if (current_user %in% paired_users$user_id) next
  
  # Get the user's top 8 candidates, excluding anyone already paired
  available_candidates <- cleaned_data %>%
    filter(user_id == current_user) %>%
    pull(candidate_id) %>%
    setdiff(c(paired_users$user_id, paired_users$paired_with))
  
  # If there's an available candidate, pair them
  if (length(available_candidates) > 0) {
    top_available <- available_candidates[1]
    # Add both directions to the paired list
    paired_users <- paired_users %>%
      add_row(user_id = current_user, paired_with = top_available) %>%
      add_row(user_id = top_available, paired_with = current_user)
  }
}

# View final unique pairs (remove duplicate entries)
final_pairs <- paired_users %>%
  distinct(user_id, paired_with) %>%
  filter(user_id < paired_with)

print(final_pairs)

Pros & Cons

  • Pros: Super straightforward, matches your "most picky first" logic, fast even for 300 users.
  • Cons: Might not give the absolute global maximum total compatibility (but it’s close enough for most use cases, especially with your top-8 limits).

2. Global Optimal Method (Maximize Total Compatibility)

If you want the absolute best possible total compatibility score across all pairs, we can use graph theory to solve this as a maximum weight perfect matching problem. This ensures no one is left out (for even numbers like 300) and the sum of all pair scores is as high as possible.

Step-by-Step Implementation

We’ll use the igraph package for this:

library(igraph)
library(tidyverse)

# First, make sure you have a full compatibility score matrix where:
# - Rows and columns are user IDs
# - score_matrix[userA, userB] = the compatibility score between userA and userB
# (You mentioned you already calculated these, so we'll use that matrix)

# Generate all unique user pairs (avoid duplicate edges)
all_pairs <- expand.grid(user1 = rownames(score_matrix), 
                         user2 = rownames(score_matrix)) %>%
  filter(user1 < user2) %>%
  # Calculate combined weight (sum of both users' scores for each other)
  mutate(weight = score_matrix[user1, user2] + score_matrix[user2, user1])

# Create an undirected graph from the pairs
penpal_graph <- graph_from_data_frame(all_pairs, directed = FALSE)

# Compute the maximum weight perfect matching
optimal_matching <- max_weight_matching(penpal_graph, type = "perfect")

# Convert the result to a clean data frame
optimal_pairs <- tibble(user_id = optimal_matching$matching[,1], 
                        paired_with = optimal_matching$matching[,2]) %>%
  filter(user_id < paired_with) %>%
  mutate(user_id = as.integer(user_id), 
         paired_with = as.integer(paired_with))

print(optimal_pairs)

Pros & Cons

  • Pros: Guarantees the highest possible total compatibility score, no arbitrary priority biases.
  • Cons: Requires a full score matrix (not just top 8), but since you already calculated scores for all pairs, this is easy to implement.

Quick Notes for Your Data

  • Double-check your raw data to fix any rows with missing X1-X8 values (your sample data has a few rows with fewer columns).
  • If you have an odd number of users, you’ll need to adjust one pair or add a "waitlist"—but 300 is even, so no problem here!

内容的提问来源于stack exchange,提问作者Anna Hoehenrieder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:26:21