【问题标题】:Algorithm to find elements best fitting in a particular amount找到最适合特定数量的元素的算法
【发布时间】:2023-03-30 03:20:01
【问题描述】:

我正在寻找一种算法来解决以下问题(我会用一个例子来解释):

假设我有 10.000 美元的可用金额和以下我可以用我的金额支付的费用:

成本 1:1.000 美元

成本 2:3.000 美元

成本 3:4.000 美元

成本 4:5.000 美元

无法部分支付费用,因此您要么支付全部费用,要么根本不支付。我正在寻找的是一种算法,它可以帮助我找到不会超过可用金额的成本组合,但另一方面会使用大部分或全部可用金额。

在我的示例中,它将是:成本 1 + 成本 3 + 成本 4。

我还想添加一个参数,该参数确定可以最大程度地资助多少成本。如果我在我的示例中说只能支付两个成本,则将返回成本 3 和成本 4。

我的方法是检查所有可用的组合,将它们相加,然后选择最能利用可用数量的组合。但是我想知道是否有一种最简单的方法可以找到最佳组合。

【问题讨论】:

  • 这听起来像是knapsack problem 的变体。
  • @Oli Charlesworth:感谢您让我知道如何调用此问题以及提供的链接。因为我总是只有很少的成本,所以找到最佳解决方案的最佳方法是我最初的方法来遍历所有可能的组合并选择最好的组合(正如 Mecki 所建议的那样)。

标签: algorithm


【解决方案1】:

这是一个简单的动态规划问题(背包的变体)。状态可以定义为 [position][rest_amount][how_many_bills_can_be_paid]。下面的递归解决方案:

假设C是成本数组并且memo被初始化为-1:

const int N = 10;    //number of notes to pay
int memo[N][M][K];   //remember to initialize it with -1

int func(int cur_index,int rest_amount,int K){

    if(cur_index == N){
        return 0;
    }

    int &ret = memo[cur_index][rest_amount][K];
    if(ret != -1) return ret;    //memoization ensures we won't solve the same sub problem more than once
    ret = 0;
    if(rest_amount >= C[cur_index]  && K > 0 )
    ret = func(cur_index+1,cost+C[cur_index],K-1);

    ret = max(ret,func(cur_index+1,cost,K);

    return ret;
}

【讨论】:

  • 恐怕我无法完全理解您的回答。是否可以将其“翻译”成 PHP 或 java,也许在您的示例中使用我的示例作为变量?对此我将不胜感激。
  • @Okizb C 与 java 非常相似,但示例中的不同之处是他在 java 中使用 int memo[N][M][K]; 而不是 int[N][M][K] memo; 声明数组,而 int func(int cur_index,int rest_amount,int K) 在 java 中是 public int func(int cur_index,int rest_amount,int K)
【解决方案2】:

对于一般数据,我无法改进您的解决方案,但是几乎没有可以加快速度的改进。

如果N的总量很低,有简单的动态解决方案:

setOfValues
put totalAmount to setOfValues
foreach cost do
    foreach amount in setOfValues do
        if (amount - cost) > 0 and not exists (amount - cost) in setOfValues
            put (amount - cost) to setOfValues
get lowest setOfValues

您可以简单地将其升级到其他要求(例如高达x 费用)。

【讨论】:

  • 你能再解释一下吗?
  • @Phpdna 这是一个与我给a while ago 另一个问题类似的算法,但语言更晦涩。我认为那里更容易跟随。
  • 搜索空间小了多少?我很难理解它。
  • @Phpdna 示例:如果有 1000000 和 100 个不同的成本,我的算法将执行大约 100kk 次操作而不是 2^100(残酷算法)并找到最佳解决方案。
  • 您的算法在 100000 次迭代中找到最优值?搜索空间小了多少百分比?
【解决方案3】:

您可以对列表进行排序并选择顶部,直到您的预算用尽。它也用于装箱,并保证在最佳范围内。

【讨论】:

  • 找不到最优值:假设总金额为 10k,成本为 6k、5k、5k。你的灵魂会选择 6k 而不是 5k+5k。
  • @Ari 正如 Phpdna 所说,它是一个近似算法(从你的例子中我会说它是一个 2 近似值)。但是,动态规划确实可以很容易地为您提供最佳解决方案。
  • 我没有说它是最优的,我写的是它在最优范围内。搜索空间太大了。这个问题也很困难。
【解决方案4】:

几周前我在java中设计了一个这样的算法。我使用了宽度为 N 的二进制矩阵,其中 N 是不同成本的数量,高度为 2^N,为我们提供了所有可能的组合。

因此,在您的情况下,第一项是指最低成本。下面是一个 1000、3000、4000 的例子。(你可以扩展这个,但我只是想告诉你我是怎么做的。)

宽度 = N = 3

高度 = 2^N = 8

0 0 0 = 0       +   0       +   0       =   0
1 0 0 = 1000    +   0       +   0       =   1000
0 1 0 = 0       +   3000    +   0       =   3000
1 1 0 = 1000    +   3000    +   0       =   4000
0 0 1 = 0       +   0       +   4000    =   4000
1 0 1 = 1000    +   0       +   4000    =   5000
0 1 1 = 0       +   3000    +   4000    =   7000
1 1 1 = 1000    +   3000    +   4000    =   8000

我有一份费用清单。当我逐行遍历矩阵时,我只是检查了矩阵上有 1 的列表。由于成本已排序,因此我发现等于或大于总金额的行将是最低的。

如果你不明白,告诉我,我会详细说明!

【讨论】:

    【解决方案5】:

    你的问题是NP problem;这个问题没有已知的解决方案可以保证在可接受的时间范围内找到最佳解决方案。真正找到最佳解决方案的唯一方法是尝试所有可能的解决方案,然后检查哪个是最佳解决方案。但是,如果您有数千个成本,那么这当然会非常缓慢。

    所有其他比上述简单方法更快的解决方案都不能保证找到最佳解决方案。他们可能会找到一个好的解决方案,但您永远无法确定是否有更好的解决方案。

    与以下问题相同:您有一辆可以运输 X 磅货物的卡车,并且您有不同重量的货物。您的目标是充分利用可用容量。这个问题也被称为“背包问题”。除了this page 中列出的解决方案之外,没有更好的解决方案。但是,它们都不能保证最佳结果。

    【讨论】:

    • 这些说法有点太强了。背包问题是weakly NP-hard,这意味着它在n(项目数)和X(目标的价值)。对于典型的应用程序,动态编程比暴力破解高效得多
    • 最近我测试了一个针对 tsp 问题的动态解决方案,效果令人印象深刻,但我仍然想知道它是如何工作的。此外,它需要更多的内存,这也可能成为一个大问题。
    • @Heuster 即使是动态规划解也可以是指数时间,因为它不是多项式解,它是一个伪多项式解。这意味着如果您的预算增加很多并且成本数量增加很多,那么复杂性的增长速度就会比实际多项式解决方案的情况快得多。动态解决方案很容易变得非常慢,只要输入值足够小,它就会很快。
    • @Mecki 绝对,我同意这一点。而聪明的剪枝可以使蛮力方法非常有效。我只是说“每一个比蛮力更快的解决方案,都不能保证给出一个最优的解决方案”是不强的。 :)
    猜你喜欢
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 2020-09-25
    相关资源
    最近更新 更多