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

求满足平均y值约束的10元素最小x和最优组合(优化高复杂度解法)

问题描述

现有500个元素,每个元素包含两个浮点属性:

  • x:取值范围0至正无穷,代表成本
  • y:取值范围0至1,代表某项指标

需要从中选取10个元素,满足两个核心条件:

  1. 选中元素的y值平均值小于给定值n
  2. 选中元素的x值总和最小(即成本最低)

目前尝试了回溯法实现,但复杂度过高,无法在合理时间内得到结果,希望找到适用于元素数量<1000场景的高效解法。当前回溯法代码如下:

def find_cheapest_combination(elements, k, target_avg):
    def backtrack(start, combo):
        if len(combo) == k:
            # 计算当前组合的y值平均值
            avg_float = sum(element[1] for element in combo) / k
            if avg_float <= target_avg:
                nonlocal best_combo, lowest_avg
                best_combo = combo[:]
                lowest_avg = avg_float
            return

        for i in range(start, len(elements)):
            if len(elements) - i < k - len(combo):
                # 剩余元素不足以凑够k个,提前终止
                break
            combo.append(elements[i])
            backtrack(i + 1, combo)
            combo.pop()

    elements.sort(key=lambda x: x[0])  # 按x值升序排序
    best_combo = []
    lowest_avg = float('inf')
    backtrack(0, [])

    return best_combo


elements = [(x, y) for x, y in items]
k = 10
target_avg = 0.07
cheapest_combination = find_cheapest_combination(elements, k, target_avg)
print(cheapest_combination)
问题分析

这个问题属于带约束的组合优化问题,确实是背包问题的变体:

  • 约束条件可转化为:选中元素的y值总和 ≤ k * target_avg(将平均值条件转为总和条件,避免浮点运算误差)
  • 目标是最小化x值总和(对应背包问题的"最小成本"目标,而非常规的"最大价值")
  • 回溯法的时间复杂度为O(C(n,k)),当n=500、k=10时,组合数约为2.5e13,完全无法在合理时间内运行,必须采用动态规划方案。
高效动态规划解法

针对元素数量<1000、k≤20的场景,我们可以通过离散化+动态规划实现高效求解:

  1. 离散化y值:将y乘以系数(如10000)转为整数,避免浮点精度问题,同时把连续的y总和转化为离散的状态空间
  2. 状态定义:dp[j][s]表示选j个元素时,y总和离散化后为s的最小x总和;额外维护prev表记录路径,用于回溯具体元素组合
  3. 状态转移:倒序遍历元素和状态,避免重复选择同一元素,更新每个状态的最小x总和

具体实现代码

def find_cheapest_combination_dp(elements, k, target_avg):
    target_sum = k * target_avg
    # 离散化y值,乘以10000转成整数,规避浮点精度误差
    scale = 10000
    max_y_sum_scaled = int(k * 1 * scale)
    target_sum_scaled = int(target_sum * scale)

    # 初始化DP表:dp[j][s] = 选j个元素、y总和离散值为s时的最小x总和
    dp = [[float('inf')] * (max_y_sum_scaled + 1) for _ in range(k + 1)]
    dp[0][0] = 0  # 基准状态:选0个元素,y总和0,x总和0

    # 维护路径回溯表:prev[j][s]记录当前状态的前驱信息
    prev = [[None] * (max_y_sum_scaled + 1) for _ in range(k + 1)]

    for elem in elements:
        x, y = elem
        y_scaled = int(y * scale)
        # 倒序遍历j和s,避免重复选择同一元素
        for j in range(k, 0, -1):
            for s in range(max_y_sum_scaled - y_scaled, -1, -1):
                if dp[j-1][s] + x < dp[j][s + y_scaled]:
                    dp[j][s + y_scaled] = dp[j-1][s] + x
                    prev[j][s + y_scaled] = (j-1, s, elem)

    # 找到满足y总和约束的最小x总和对应的状态
    min_x_sum = float('inf')
    best_s = -1
    for s in range(target_sum_scaled + 1):
        if dp[k][s] < min_x_sum:
            min_x_sum = dp[k][s]
            best_s = s

    if best_s == -1:
        return []  # 无符合条件的组合

    # 回溯路径得到具体元素组合
    combo = []
    current_j, current_s = k, best_s
    while current_j > 0:
        p_j, p_s, elem = prev[current_j][current_s]
        combo.append(elem)
        current_j, current_s = p_j, p_s
    combo.reverse()

    return combo


# 示例调用
items = [(1.2, 0.05), (2.1, 0.06), (0.8, 0.08)]  # 替换为你的元素列表
k = 10
target_avg = 0.07
cheapest_combination = find_cheapest_combination_dp(items, k, target_avg)
print(cheapest_combination)

解法说明

  • 时间复杂度:O(n * k * S),其中n为元素数量,k为选取元素数,S为离散化后的y总和最大值(k*10000)。对于n=500、k=10,总运算量约5e7,Python中可快速完成
  • 空间优化:若不需要记录具体组合,可将二维DP数组优化为一维;若需组合则需维护prev表
  • 精度控制:通过离散化处理避免浮点运算的精度丢失,确保约束判断准确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 23:25:58