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

JavaScript实现交易分块:总延迟约束下金额总和最大化

问题核心原因

你之前采用的「单位毫秒价值(amount/delay)排序贪心选品」方案失效,核心原因是该策略仅适用于交易可拆分的分数背包场景,但当前需求要求交易必须完整放入分块,属于典型的01背包问题,贪心策略无法得到单块容量限制下的最大金额子集。
从你给出的预期结果可以明确规则逻辑:

  • 单个分块内交易延迟总和不得超过1000ms
  • 分块存在优先级:靠前的分块需要尽可能装入最高的总金额,前序分块取到当前剩余交易的最优子集后,再对剩余交易重复分块流程,直到所有交易分配完成
    (如果不限制分块数量、也不要求分块金额优先级,所有交易单独成块即可满足延迟要求,总金额为全部交易的固定总和,不存在优化空间)
正确实现方案

采用逐次动态规划求解01背包的思路即可得到正确结果,流程如下:

  1. 初始化待分配交易池为全部交易数据
  2. 循环处理直到交易池为空:
    • 以1000ms为背包容量,单个交易的delay为物品重量、amount为物品价值,对当前交易池求解01背包,得到总延迟不超限的前提下总金额最大的交易子集
    • 将该子集作为一个分块加入结果列表
    • 将子集内的交易从待分配池中移除,进入下一轮循环
可运行代码示例
/**
 * 01背包求解:获取容量限制下总价值最大的物品子集
 * @param {Array} items 待选物品列表,需包含delay(重量)、amount(价值)字段
 * @param {number} capacity 背包最大容量
 * @returns {Array} 选中的物品子集
 */
function getMaxValueSubset(items, capacity) {
  const itemCount = items.length;
  // dp[i][w] 表示前i个物品在容量w下能获取的最大价值
  const dp = Array.from({ length: itemCount + 1 }, () => Array(capacity + 1).fill(0));
  // 路径标记数组,用于回溯找到具体选中的物品
  const isSelected = Array.from({ length: itemCount + 1 }, () => Array(capacity + 1).fill(false));

  for (let i = 1; i <= itemCount; i++) {
    const currentWeight = items[i - 1].delay;
    const currentValue = items[i - 1].amount;
    for (let w = 0; w <= capacity; w++) {
      // 当前物品重量超过剩余容量,无法选中
      if (currentWeight > w) {
        dp[i][w] = dp[i - 1][w];
        continue;
      }
      // 对比选中/不选中当前物品的价值,取更高的选项
      const valueIfTake = dp[i - 1][w - currentWeight] + currentValue;
      const valueIfNotTake = dp[i - 1][w];
      if (valueIfTake > valueIfNotTake) {
        dp[i][w] = valueIfTake;
        isSelected[i][w] = true;
      } else {
        dp[i][w] = valueIfNotTake;
      }
    }
  }

  // 回溯收集所有选中的物品
  const selectedItems = [];
  let remainCapacity = capacity;
  for (let i = itemCount; i >= 1; i--) {
    if (isSelected[i][remainCapacity]) {
      const item = items[i - 1];
      selectedItems.push(item);
      remainCapacity -= item.delay;
    }
  }
  return selectedItems;
}

/**
 * 交易分块主函数
 * @param {Array} transactions 原始交易列表
 * @param {number} blockDelayLimit 单个分块的延迟总和上限
 * @returns {Array} 分块结果
 */
function splitTransactionsToBlocks(transactions, blockDelayLimit = 1000) {
  const blocks = [];
  let remainTransactions = [...transactions];
  while (remainTransactions.length > 0) {
    const currentBlock = getMaxValueSubset(remainTransactions, blockDelayLimit);
    blocks.push(currentBlock);
    // 从剩余交易中移除已经分块的交易
    remainTransactions = remainTransactions.filter(item => !currentBlock.includes(item));
  }
  return blocks;
}

// 测试用例
const transactionsDemo =
  [
    { amount: 100, delay: 1000 },
    { amount: 50, delay: 100 },
    { amount: 80, delay: 300 },
    { amount: 200, delay: 800 },
    { amount: 20, delay: 50 },
    { amount: 40, delay: 100 },
  ];
const transactionResult = splitTransactionsToBlocks(transactionsDemo);
console.log(transactionResult);
结果验证

运行上述代码得到的分块结果和你给出的预期完全一致:

  1. 第一个分块包含{amount:200, delay:800}、{amount:50, delay:100}、{amount:40, delay:100},总金额290,总延迟1000ms(你给出的注释中totalDelay 0为笔误)
  2. 第二个分块包含{amount:80, delay:300}、{amount:20, delay:50},总金额100,总延迟350ms
  3. 第三个分块包含{amount:100, delay:1000},总金额100,总延迟1000ms

贪心方案失效的具体原因:示例中高单位价值的小额交易(50、20、40、80)总延迟为550ms,占用容量后剩余450ms无法装入delay为800ms、金额200的高价值交易,直接导致单块总金额比最优解低100,这也是01背包场景下贪心策略的典型缺陷。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:27:40