【问题标题】:Algorithm to pick values from array that sum closest to a target value?从数组中选择总和最接近目标值的值的算法?
【发布时间】:2011-03-09 05:40:10
【问题描述】:

我有一个 几乎 排序值 28 个元素长的数组。我需要找到与提供给算法的目标值相加的一组值(或者如果找不到确切的总和,则最接近的总和低于目标值)。

我目前有一个简单的算法可以完成这项工作,但它并不总能找到最佳匹配。它在具有一组特定值的理想情况下工作,但我需要一个更强大、更准确的解决方案来处理更广泛的数据集。

算法必须用 C 而不是 C++ 编写,并且适用于嵌入式系统,因此请记住这一点。

这是我当前的算法供参考。它从可用的最高值开始迭代。如果当前值小于目标总和,则将该值添加到输出并从目标总和中减去。重复此过程,直到达到总和或用完值。它假定一个几乎升序的排序列表。

//valuesOut will hold a bitmask of the values to be used (LSB representing array index 0, next bit index 1, etc)

void pickValues(long setTo, long* valuesOut)
{
    signed char i = 27;//last index in array
    long mask = 0x00000001;

    (*valuesOut) = 0x00000000;
    mask = mask<< i;//shift to ith bit
    while(i>=0 && setTo > 0)//while more values needed and available
    {
        if(VALUES_ARRAY[i] <= setTo)
        {
            (*valuesOut)|= mask;//set ith bit
            setTo = setTo - VALUES_ARRAY[i]._dword; //remove from remaining         }
        //decrement and iterate
        mask = mask >> 1;
        i--;
    }
}

更多参数:

  • 值数组很可能按升序接近排序,但无法强制执行,因此假设没有排序。其实也可能有重复值。

  • 数组很可能会保存一组值,这些值无法创建其范围内的每个总和。如果无法找到准确的总和,算法应返回创建下一个最低总和的值。

【问题讨论】:

  • 集合中的数字是否不同? IE。同一个数字可以出现多次吗?
  • @torak 否。同一个数字可能会出现多次
  • @torak 值都是正数

标签: c algorithm math embedded


【解决方案1】:

这个问题被称为子集和问题,它是Knapsack problem 的一个特例。维基百科是一些算法的良好起点。

【讨论】:

  • 它接近于 0-1 背包问题,但并不完全。背包问题在给定重量限制、给定一组物品及其重量的情况下最大化包装物品的数量。我的目标是在给定一组物品及其重量的情况下最大化重量。
  • @CSharperWithJava ... 被称为子集和问题,是背包问题的一个特例。您正在寻找一个近似的子集总和。
  • @Karmastan 感谢您的更正。是的,这就是我要找的。最大化(总和(w*x))
  • 无论如何,这个问题同样是 NP 完全的,这意味着没有算法可以既正确又快速。要么不正确且快速,要么正确且非常慢;-)
【解决方案2】:

正如其他人所指出的,这与子集和问题的优化版本相同,即 NP-Complete。

由于您提到您的内存不足并且可能使用近似解决方案(基于您当前的解决方案),因此存在多项式时间近似算法来解决子集和的优化版本。

例如,给定 e > 0,有一个多项式时间算法使用 O((n*logt)/e) 空间,(t 是目标总和,n 是数组的大小)它给你一个子集,使得总和 z 不小于最优值的 1/(1+e) 倍。

即如果最大的子集和是 y,那么算法找到一个子集和 z 使得

z <= y <= (1+e)z

并使用空间 O((n*logt)/e)。

这样的算法可以在这里找到:http://www.cs.dartmouth.edu/~ac/Teach/CS105-Winter05/Notes/nanda-scribe-3.pdf

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    如果值相当小,则它是一个简单的动态规划 (DP)。时间复杂度为 O(n * target),内存需求为 O(target)。如果这让你满意,网上有很多 DP 教程。例如,这里第一个讨论的问题(使用计数)与您的非常相似(除了它们允许多次使用每个数字):
    http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg

    更新 是的,正如其他人所说,这是一个简单的背包问题。

    【讨论】:

    • 这个概念很有趣,谢谢你指点我。不幸的是,正如我在 OP 中所说,这是一个嵌入式应用程序,我没有 O(target) 个字节可供备用。
    • @CSharperWithJava 如果使用位,您可以将内存需求减少到 'target/8' 字节,但仅此而已。否则,您只能使用“近似”解决方案(除非您想检查所有 2^28 个组合)。
    猜你喜欢
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    • 2021-12-16
    • 2019-01-12
    • 2017-04-04
    • 2016-10-10
    • 2016-10-03
    相关资源
    最近更新 更多