【问题标题】:Maximise the profit of shares (Maximum subarray)最大化股票的利润(最大子数组)
【发布时间】:2016-07-12 06:48:34
【问题描述】:

我正在阅读“算法简介”。在最大子数组问题(第 4 章)中,作者声称不能仅通过找到数组的最大值和最小值来计算买卖股票的最大利润。作者通过计算需要 0(n^2) 时间的买入和卖出日期的所有可能组合来谈到诸如蛮力之类的替代方案。另一种选择是找到价格数组中每日变化的最大子数组。

但是,我编写了一个算法,该算法将花费 0(n) 时间并找到最大利润。这是 0(n) 与 0(n log n) 的最大子数组问题。但我知道作者不会错。我哪里错了?我的算法正确吗?

#include <stdio.h>
#include <limits.h>

int main(void) 
{
    int A[8]={10,8,3,9,7,4,5,10,4};
    int i,max,min,currmax,prevmax,n=8;

    max=INT_MIN;
    min=INT_MAX;
    for(i=0;i<n;i++)
    {
        if(A[i]>max)
        {
            max=A[i];
            prevmax=currmax;
            currmax=max-min;
        }
        if(A[i]<min)
        {
            min=A[i];
            max=INT_MIN;
        }

    }

    if(prevmax>currmax)
        printf("%d",prevmax);
    else
        printf("%d",currmax);

    return 0;
}

如 GeeksForGeeks (http://www.geeksforgeeks.org/divide-and-conquer-maximum-sum-subarray/) 中所述,股票价格每日变化的输入为 A[ ]={-2, -5, 6, -2, -3, 1, 5, -6}。 我将基价设为 10,并将算法运行为 A[]={10,8,3,9,7,4,5,10,4};

输入:10,8,3,9,7,4,5,10,4

输出:7

【问题讨论】:

  • 如你所说,最大序列问题给出了解决方案。但是最大序列问题是solved 在 O(n) 空间和 O(1) 时间。它在哪里(哪个页面)说 O(n log n)?
  • 你的算法似乎有正确的想法。但我认为你应该取出 prevmax 而不是 if (max-min &gt; currmax) currmax = max-min; 并打印 currmax 作为你的答案。否则,如果答案出现在 prevmax 之前,您将是不正确的。另外你应该将currmax初始化为0,以防没有利润。
  • @LawrenceWu:第 74 页,CLRS 3E。它说0(n lg n)。是的,不需要 prevmax。
  • @AbhishekBansal:输出仍然是 7。但是我现在更改了输入。
  • 阅读最后一句话,它说分而治之不是最好的,但 O(n) 是可以实现的,这就是你基本上所做的。 “如练习 4.1-5 所示,实际上存在线性时间算法 [...]”

标签: algorithm


【解决方案1】:

您正在寻找通过一次买卖股票可以获得的最大收益(一次很重要)。

您的算法假设最佳利润来自股票的最低执行价。 这是错误的:例如使用A[] = {10,23,6,12,4,1,0,3},您的程序输出 3 而最大收益显然是 13(Coliru 上的证明)

最佳收益的一个特征是计算股票定价的增量数组(您引用的A[ ]={-2, -5, 6, -2, -3, 1, 5, -6} 数组),然后获得最大子数组。在我的示例中,我们得到F[] = {13, -17, 6, -8, -3, -1, 3},最大子数组为{13}。最后,所需的利润是最大子数组的总和。

查看它的另一种方法是迭代地保留数组A[0..i-1] 的最小值(i 范围从 1 到 n-1)。在指数i(即在时间i 卖出),最佳利润是差值A[i] - min(A[0..i-1]),如果差值为负,则为0。您现在必须跟踪这些差异的最大值。最后,上述最大值就是我们想要的结果。

这基本上就是您的算法所做的。更清晰的实现见running here

#include <stdio.h>
#include <limits.h>

int main(void) 
{
    int A[8]={10,23,6,12,4,1,0,3};
    int i,min_i, profit_i, best_profit=0,n=8;

    min_i=A[0];
    for(i=1;i<n;i++)
    {
        profit_i = A[i] - min_i;
        best_profit = (profit_i > best_profit) ? profit_i : best_profit;

        if(A[i]<min_i)
        {
            min_i=A[i];
        }

    }

    printf("Best profit: %d", best_profit);
    return 0;
}

有不必要的局部变量,但我发现它们使逻辑更容易看到。

【讨论】:

  • 这个假设只是部分错误。在时间 t 卖出的最佳利润将来自时间 t 之前的最低价格。因此,仍然可以挽救 OP 的解决方案。 delta 数组不一定是“最佳表征”,因为取差异然后计算最佳总和最多是浪费精力(这些操作无论如何都是逆向的),最坏的情况是浪费 O(n) 内存。
  • 如@LawrenceWu 所指出的那样,代码输出为 13,与(max-min &gt; currmax) currmax = max-min; 和没有prevmax 一起使用。
  • @LawrenceWu 这将需要计算一个大小为n-1 的数组,其中如果股票在t 时卖出,则索引t 处的元素将是“最佳利润”,然后获取该数组的最大值。仍然浪费O(n) 内存。计算所述数组是O(n),但是它比最大子数组更好
  • 不需要计算这样的数组,因为我们不关心过去的值。您只需要跟踪一个变量,min(与 Kadane 的算法只需要 O(1) 内存来获得最大子序列的原因相同)。在每次迭代中执行min = std::min(min, price[i])profit = std::max(profit, price[i] - min),最后返回profit
  • 我只是发布它,因为它在你的答案中丢失了,所以一旦你合并它就不需要了。
猜你喜欢
  • 2021-04-22
  • 2019-12-22
  • 2016-08-29
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
相关资源
最近更新 更多