矩阵拆分组合最小和求解及大规模数据集的Python/R实现咨询
问题分析
先把你给出的示例矩阵整理成更清晰的表格形式:
| A | B | C | D | E | F | |
|---|---|---|---|---|---|---|
| A | - | 12 | 3 | 5 | 7 | 9 |
| B | - | - | 8 | 11 | 6 | 2 |
| C | - | - | - | 10 | 1 | 7 |
| D | - | - | - | - | 17 | 1 |
| E | - | - | - | - | - | 19 |
| F | - | - | - | - | - | - |
你的核心需求是:将一个包含2700个节点的完全图(边权由矩阵给出)划分为13个大小为200的子集和1个大小为100的子集,使得所有子集内部的边权总和最小(等价于子集间边权总和最大)。之前尝试用combinat包的comboGeneral处理小规模数据可行,但面对2700节点的大规模数据时完全无法运行;comboGroups又不支持自定义目标函数,所以需要更高效的近似算法方案。
解决方案思路
由于这是NP难的图划分问题,暴力枚举所有分组组合完全不现实,我们需要用启发式或近似算法来获取最优解(或近似最优解)。下面分别给出R和Python的可行方案:
R语言方案
1. 遗传算法(GA包)
遗传算法非常适合这类组合优化问题,我们可以用GA包来实现:
首先,先把原上三角矩阵转换成对称矩阵,方便统一计算所有组内两两元素的边权和:
library(GA) # 假设你的矩阵名为mat,先转换为对称矩阵 mat_sym <- mat mat_sym[lower.tri(mat_sym)] <- t(mat_sym)[lower.tri(mat_sym)] diag(mat_sym) <- 0 # 对角线值不计入总和 # 定义分组大小约束 group_sizes <- c(rep(200, 13), 100) n_nodes <- sum(group_sizes) # 目标函数:计算组内边权总和,GA默认最大化适应度,所以返回负值实现最小化 fitness <- function(x) { total <- 0 for (g in unique(x)) { members <- which(x == g) # 对称矩阵会重复计算边权,所以除以2 total <- total + sum(mat_sym[members, members]) / 2 } return(-total) } # 约束函数:确保每个组的大小符合要求 constraint <- function(x) { tbl <- table(x) all(tbl == group_sizes) } # 运行遗传算法,可根据计算资源调整种群大小和迭代次数 ga_result <- ga(type = "permutation", fitness = fitness, lower = 1, upper = 14, nBits = n_nodes, constraints = list(constraint), popSize = 50, maxiter = 100, run = 20, parallel = TRUE) # 获取最优分组结果 best_grouping <- ga_result@solution[1, ]
2. 贪心+局部优化
如果遗传算法运行速度太慢,也可以先尝试贪心策略快速得到近似解,再通过局部交换优化:
# 先随机初始化符合大小要求的分组 initial_groups <- split(sample(1:n_nodes), rep(1:14, group_sizes)) # 定义局部优化函数:随机交换两个组的节点,若能降低总组内和则保留 improve_grouping <- function(groups, mat) { total_current <- sum(sapply(groups, function(g) sum(mat[g, g])/2)) # 随机选两个不同的组 g1_idx <- sample(1:14, 1) g2_idx <- sample(setdiff(1:14, g1_idx), 1) # 随机选一个节点交换 node1 <- sample(groups[[g1_idx]], 1) node2 <- sample(groups[[g2_idx]], 1) # 生成新分组 new_g1 <- c(setdiff(groups[[g1_idx]], node1), node2) new_g2 <- c(setdiff(groups[[g2_idx]], node2), node1) new_groups <- groups new_groups[[g1_idx]] <- new_g1 new_groups[[g2_idx]] <- new_g2 # 计算新总和,判断是否保留 total_new <- sum(sapply(new_groups, function(g) sum(mat[g, g])/2)) if (total_new < total_current) return(new_groups) else return(groups) } # 迭代优化多次 current_groups <- initial_groups for (i in 1:10000) { current_groups <- improve_grouping(current_groups, mat_sym) } # 最终分组结果 final_groups <- current_groups
Python语言方案
1. 专门图划分工具(METIS)
METIS是工业界常用的高效图划分工具,针对大规模数据优化,推荐优先尝试:
import pymetis import numpy as np # 转换为对称矩阵 mat_sym = mat + mat.T np.fill_diagonal(mat_sym, 0) # 构建METIS需要的邻接表 adj_list = [np.where(row > 0)[0].tolist() for row in mat_sym] # 划分成14组,指定每组大小(注意METIS的参数格式) n_cuts, membership = pymetis.part_graph(14, adjacency=adj_list, node_sizes=[200]*13 + [100]) # membership是每个节点对应的组编号(0-13),可进一步整理成分组列表 final_groups = [[] for _ in range(14)] for node, g in enumerate(membership): final_groups[g].append(node)
2. 遗传算法(DEAP包)
DEAP是Python灵活的进化算法框架,可自定义实现遗传算法:
from deap import base, creator, tools, algorithms import random import numpy as np # 初始化问题:最小化组内边权总和 creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", list, fitness=creator.FitnessMin) # 矩阵转换为对称矩阵 mat_sym = mat + mat.T np.fill_diagonal(mat_sym, 0) n_nodes = 2700 group_sizes = [200]*13 + [100] n_groups = len(group_sizes) # 初始化工具集 toolbox = base.Toolbox() toolbox.register("attr_int", random.randint, 0, n_groups-1) toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_int, n=n_nodes) toolbox.register("population", tools.initRepeat, list, toolbox.individual) # 评估函数 def evaluate(individual): total = 0 for g in range(n_groups): members = [i for i, val in enumerate(individual) if val == g] total += np.sum(mat_sym[np.ix_(members, members)]) / 2 return (total,) # 约束函数 def check_constraint(individual): counts = [individual.count(g) for g in range(n_groups)] return counts == group_sizes toolbox.register("evaluate", evaluate) toolbox.register("mate", tools.cxTwoPoint) toolbox.register("mutate", tools.mutUniformInt, low=0, up=n_groups-1, indpb=0.05) toolbox.register("select", tools.selTournament, tournsize=3) # 运行遗传算法 pop = toolbox.population(n=50) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("avg", np.mean) stats.register("min", np.min) pop, log = algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=100, stats=stats, halloffame=hof, verbose=True) # 最优分组结果 best_individual = hof[0]
注意事项
- 对于2700节点的大规模数据,建议开启并行计算(R的GA包和Python的DEAP都支持),利用多核CPU加速。
- 可以结合多种算法:先用贪心或METIS得到初始解,再用遗传算法做局部优化,平衡效率和结果精度。
- 如果对结果精度要求极高,可尝试模拟退火算法,它在局部优化上表现稳定。
内容的提问来源于stack exchange,提问作者user3247230
相关产品推荐
相关产品推荐

