0

0

基于均值优化的数据集子集划分:混合整数规划与启发式方法

霞舞

霞舞

发布时间:2025-09-20 12:02:17

|

317人浏览过

|

来源于php中文网

原创

基于均值优化的数据集子集划分:混合整数规划与启发式方法

本文探讨如何将一个超集(数据集)划分为N个指定大小的子集,同时确保每个子集的均值尽可能接近超集的总均值,且元素不重复使用。我们主要介绍如何将此问题建模为混合整数线性规划(MILP),并使用Python的PuLP库进行求解,以实现精确的均值优化。同时,文章也讨论了在面对大规模数据时的性能挑战及潜在的启发式优化策略。

1. 问题描述与挑战

在数据分析、实验设计或样本分配等场景中,我们经常需要将一个包含m个元素的原始数据集(超集)划分为n个互不重叠、且大小预定的子集。一个常见的优化目标是使每个子集的统计特性(例如均值)尽可能地与原始超集的特性保持一致。具体来说,给定一个超集 s 及其包含的 m 个元素,以及 n 个预期的子集大小 x0, x1, ..., xn-1(其中 sum(xi) == m),目标是创建这些子集,使得每个子集的均值与超集的均值最为接近。我们通常通过最小化所有子集均值与超集均值之间绝对差异的总和来量化这一目标。

这是一个典型的组合优化问题,其挑战在于:

  • 无放回抽样: 超集中的每个元素只能被分配到一个子集中,且仅使用一次。
  • 固定子集大小: 每个子集必须严格满足其预设的元素数量。
  • 均值优化: 这是一个全局优化目标,需要权衡不同子集之间的分配,以达到整体最优。
  • 计算复杂度: 随着超集元素数量和子集数量的增加,可能的组合呈指数级增长,导致穷举法不可行。在实际应用中,算法需要在合理的时间内(例如1秒内)完成对中等规模数据的处理。

2. 数学建模:混合整数线性规划 (MILP)

这种类型的分配问题可以被归类为“集合划分问题”(Set Partitioning Problem)的一个变种,其中加入了特定的目标函数(均值优化)和额外的约束。混合整数线性规划(MILP)提供了一种强大的框架来精确解决这类问题。

2.1 目标函数

我们的目标是使每个子集的均值 mean(subset_s) 尽可能接近超集的均值 mean(superset)。这等价于使每个子集的总和 sum(subset_s) 尽可能接近 subset_size_s * mean(superset)。因此,我们可以定义目标函数为最小化所有子集总和与目标总和之间绝对差异的总和:

$$ \min \sum{s=0}^{N-1} | \sum{i \in \text{subset}_s} \text{element}_i - (\text{size}_s \times \text{mean}(\text{superset})) | $$

2.2 决策变量

我们引入二元决策变量 x_s_i: x_s_i = 1 如果超集中的第 i 个元素被分配到第 s 个子集中。 x_s_i = 0 否则。

2.3 约束条件

  1. 子集大小约束: 每个子集 s 必须包含预定数量的元素 size_s。 $$ \sum{i=0}^{M-1} x{s,i} = \text{size}_s \quad \forall s \in {0, \dots, N-1} $$
  2. 元素唯一性约束: 超集中的每个元素 i 只能被分配到一个且仅一个子集中。 $$ \sum{s=0}^{N-1} x{s,i} = 1 \quad \forall i \in {0, \dots, M-1} $$
  3. 绝对值线性化: 在线性规划中,通常通过引入辅助变量和不等式来处理绝对值。对于每个子集 s,我们定义其总和误差 err_s: $$ \text{err}s = \sum{i=0}^{M-1} (\text{element}i \times x{s,i}) - (\text{size}_s \times \text{mean}(\text{superset})) $$ 然后引入一个非负辅助变量 abs_err_s 来表示 |err_s|,并添加以下约束: $$ \text{abs_err}_s \ge \text{err}_s $$ $$ \text{abs_err}_s \ge -\text{err}_s $$ 最终,目标函数变为最小化 sum(abs_err_s)。

3. 使用 PuLP 进行求解

PuLP 是一个用 Python 编写的线性规划建模工具,它允许用户以直观的方式定义优化问题,并调用各种求解器(如CBC、GLPK、Gurobi等)来解决。

以下是一个使用 PuLP 解决上述问题的示例代码:

from statistics import mean
import pulp

def solve_subset_partitioning(superset_elements, subset_sizes):
    """
    使用 PuLP 解决基于均值优化的数据集子集划分问题。

    Args:
        superset_elements (list): 超集中的所有元素列表。
        subset_sizes (list): N个子集各自的目标大小列表。

    Returns:
        tuple: (list of lists, list of floats) 分割后的子集元素列表和每个子集的均值。
    """

    N = len(subset_sizes)
    M = len(superset_elements)

    # 验证输入
    if sum(subset_sizes) != M:
        raise ValueError("所有子集大小之和必须等于超集元素总数。")

    # 计算超集均值
    superset_mean = mean(superset_elements)

    # 创建 PuLP 问题实例
    set_partitioning_model = pulp.LpProblem("Set_Partitioning_Model", pulp.LpMinimize)

    # 决策变量:x_s_i = 1 如果超集中的第 i 个元素被分配到第 s 个子集中
    # covering[s] 是一个列表,其中包含子集 s 的 M 个二元变量
    covering = {}
    for s in range(N):
        vals = []
        for i, v in enumerate(superset_elements):
            vals.append(
                pulp.LpVariable(
                    f"x_set_{s}_element_idx_{i:>02}_val_{v}",
                    lowBound=0,  # 0
                    upBound=1,   # 1
                    cat=pulp.LpBinary, # 二进制变量
                )
            )
        covering[s] = vals

    # 辅助变量:用于处理绝对误差
    abs_sum_errs = []
    for s_i in range(N):
        abs_sum_errs.append(pulp.LpVariable(f"set_{s_i}_sum_error_abs", lowBound=0))

    # 目标函数:最小化所有子集绝对误差之和
    set_partitioning_model += pulp.lpSum(abs_sum_errs), "Minimize_Absolute_Sum_Errors"

    # 添加约束
    for s_i, st_vars in covering.items():
        # 计算每个子集的实际总和
        current_set_sum = pulp.lpSum([p * superset_elements[i] for i, p in enumerate(st_vars)])

        # 计算每个子集的目标总和 (子集大小 * 超集均值)
        target_set_sum = subset_sizes[s_i] * superset_mean

        # 定义子集总和误差变量
        set_sum_err = pulp.LpVariable(f"set_{s_i}_sum_error")
        set_partitioning_model += set_sum_err == current_set_sum - target_set_sum, f"Set_{s_i}_Sum_Error_Definition"

        # 绝对值线性化约束
        set_partitioning_model += abs_sum_errs[s_i] >= set_sum_err, f"Abs_Error_Upper_Bound_Pos_{s_i}"
        set_partitioning_model += abs_sum_errs[s_i] >= -set_sum_err, f"Abs_Error_Upper_Bound_Neg_{s_i}"

    # 约束1: 每个子集的大小必须符合预设
    for s_i, st_vars in enumerate(covering.values()):
        set_partitioning_model += pulp.lpSum(st_vars) == subset_sizes[s_i], f"Set_{s_i}_Size_Constraint"

    # 约束2: 超集中的每个元素只能被使用一次
    # zip(*covering.values()) 将所有子集的变量列表转置,以便按元素索引迭代
    for i, element_vars in enumerate(zip(*covering.values())):
        set_partitioning_model += (
            pulp.lpSum(element_vars) == 1,
            f"Element_{i}_Used_Once_Constraint",
        )

    # 求解模型
    set_partitioning_model.solve(pulp.PULP_CBC_CMD(msg=False)) # 使用默认的CBC求解器,静默模式

    # 提取结果
    if pulp.LpStatus[set_partitioning_model.status] == "Optimal":
        result_subsets = []
        result_means = []
        for s_i, st_vars in covering.items():
            current_subset_elements = [
                superset_elements[i] for i, var in enumerate(st_vars) if var.value() == 1
            ]
            result_subsets.append(current_subset_elements)
            result_means.append(mean(current_subset_elements))
        return result_subsets, result_means
    else:
        print(f"未能找到最优解。状态: {pulp.LpStatus[set_partitioning_model.status]}")
        return [], []

# 示例 1:完美分配
print("--- 示例 1:完美分配 ---")
superset1 = [100]*5 + [101]*10 + [102]*5
subset_sizes1 = [2, 4, 14]
subsets1, means1 = solve_subset_partitioning(superset1, subset_sizes1)

print(f"超集均值: {mean(superset1)}")
for i, (subset, mean_val) in enumerate(zip(subsets1, means1)):
    print(f"子集 {chr(65+i)} ({len(subset)} 元素): {subset}, 均值: {mean_val}")
# 预期输出:所有子集均值均为 101

# 示例 2:最佳拟合
print("\n--- 示例 2:最佳拟合 ---")
superset2 = [100]*5 + [103]*10 + [104]*5
subset_sizes2 = [2, 4, 14]
subsets2, means2 = solve_subset_partitioning(superset2, subset_sizes2)

print(f"超集均值: {mean(superset2)}")
for i, (subset, mean_val) in enumerate(zip(subsets2, means2)):
    print(f"子集 {chr(65+i)} ({len(subset)} 元素): {subset}, 均值: {mean_val}")
# 预期输出:子集均值尽可能接近 102.5

代码解析:

  1. 初始化: 定义超集元素、子集大小,并计算超集均值。
  2. LpProblem: 创建一个 PuLP 问题实例,目标是最小化 (pulp.LpMinimize)。
  3. 决策变量 covering: 这是一个字典,键是子集索引 s,值是一个列表,包含了 M 个 pulp.LpBinary 变量。每个变量 x_s_i 代表超集中的第 i 个元素是否属于第 s 个子集。
  4. 辅助变量 abs_sum_errs: 用于存储每个子集总和与目标总和之间绝对差异的辅助变量,lowBound=0 确保其非负。
  5. 目标函数: 设置为最小化 abs_sum_errs 中所有元素的和。
  6. 误差定义与绝对值约束: 遍历每个子集,计算其目标总和 (subset_size * superset_mean)。然后定义 set_sum_err 为实际总和与目标总和之差。最后,通过两个不等式 abs_sum_errs[s_i] >= set_sum_err 和 abs_sum_errs[s_i] >= -set_sum_err 来实现绝对值的线性化。
  7. 子集大小约束: 确保每个子集中的元素数量(即对应 x_s_i 变量之和)等于其预设的 subset_sizes[s_i]。
  8. 元素唯一性约束: 确保超集中的每个元素 i(通过 zip(*covering.values()) 遍历)在所有子集中的 x_s_i 变量之和为1,即每个元素仅被分配一次。
  9. 求解: 调用 set_partitioning_model.solve() 启动求解器。默认情况下,PuLP 会使用其自带的 CBC 求解器。
  10. 结果提取: 检查求解状态,如果找到最优解,则遍历决策变量,提取每个子集包含的元素,并计算其均值。

4. 启发式算法:Karmarkar-Karp (Largest Differencing Method)

当精确求解(如MILP)因问题规模过大而变得不可行时,启发式算法提供了一种快速获得近似解的方法。Karmarkar-Karp 算法(也称为最大差分法)是解决数集划分问题的一种著名启发式算法,其目标是将一个数集划分为 k 个子集,使这些子集的和尽可能接近,即最小化最大子集和与最小子集和之间的差异。

PNG Maker
PNG Maker

利用 PNG Maker AI 将文本转换为 PNG 图像。

下载

优点: 速度快,易于实现。 局限性:

  • 无法指定子集大小: Karmarkar-Karp 算法的主要目的是平衡子集和,而不是严格控制子集中的元素数量。这与我们问题中“固定子集大小”的要求不符。
  • 不直接优化均值: 尽管它试图使子集和接近,但这并不直接等同于使子集均值接近超集均值,尤其是在子集大小不固定的情况下。

因此,Karmarkar-Karp 算法不适用于严格满足本教程最初提出的所有约束条件。然而,作为一种通用的数集划分启发式方法,它在某些场景下仍有其价值,例如当我们只需要大致平衡子集总和,而对子集大小没有严格要求时。

以下是一个使用 numberpartitioning 库实现 Karmarkar-Karp 算法的示例:

from statistics import mean
from numberpartitioning import karmarkar_karp

# 示例 2 的超集数据
superset = [100, 100, 100, 100, 100, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104]

print("\n--- 启发式方法:Karmarkar-Karp ---")
print("超集均值:", mean(superset))

# 使用 Karmarkar-Karp 划分成 3 个部分
# 注意:此方法不接受预设的子集大小
for p in karmarkar_karp(superset, num_parts=3).partition:
    print(f"子集 ({len(p)} 元素): {p}, 均值: {mean(p)}")

从输出可以看出,Karmarkar-Karp 算法生成的子集大小不固定,且其均值与超集均值的接近程度可能不如 MILP 得到的精确解。

5. 性能考量与优化策略

尽管 MILP 可以提供最优解,但其计算复杂度是 NP-hard 的。对于大规模问题,求解时间可能过长。在实际应用中,我们需要根据具体情况权衡求解精度和计算效率。

  • 问题规模:

    • N (子集数量): 10-25 个子集通常是 MILP 可处理的范围,但达到 100 个子集时,问题会变得非常困难。
    • M (超集元素数量): 100-1000 个元素是常见情况,10000 个唯一元素则非常庞大。
    • 唯一元素数量: 如果超集中有大量重复元素,可以考虑预处理,将相同元素视为一个“类别”,并为每个类别分配一定数量的元素到子集,这可能简化问题。
  • 优化策略:

    1. 启发式预分配 + 精确调整:
      • 初步均匀分配: 按照子集大小比例,从超集中随机(或均匀)抽取元素填充子集 50%-7

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

407

2023.08.14

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

31

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

10

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

32

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

1

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

3

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

8

2026.01.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

23

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

122

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.3万人学习

Django 教程
Django 教程

共28课时 | 3.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号