最大化给定股票报价的利润[关闭]

Posted

技术标签:

【中文标题】最大化给定股票报价的利润[关闭]【英文标题】:Maximizing profit for given stock quotes [closed] 【发布时间】:2012-03-19 19:53:02 【问题描述】:

我在为一家初创公司面试时被问到这个问题,并在最近的比赛中再次看到这个问题

Code Sprint:systems

**问题:

给你一组天的股票价格。每天,您可以购买一个单位的股票,出售您已经购买的任意数量的股票单位,或者什么也不做。通过优化您的交易策略,您可以获得的最大利润是多少?**

示例(输入,即天数可能会有所不同)

5 3 2 => 利润 = 0 // 由于价格每天都在下降,我们可以赚取的最大利润 = 0

1 2 100 => 利润 = 197

1 3 1 2 =>profit = 3 // 我们以 1 买入,以 3 卖出,然后以 1 买入并以 2 卖出 ..总利润 = 3

我的解决方案:

a) 找出股票价格最高的那一天。直到那天继续购买 1 单位的股票。

b) 如果那一天是最后一天,则退出:

其他: 卖出当天的所有股票,并在当天之后拆分数组并递归剩余元素 c) 合并利润

例如 1 4 1 2 3 a) 第 2 天的最高股价 .. 所以我们在第 1 天买入股票并在第 2 天卖出(利润 = 3)然后我们在剩余的日子里递归:1 2 3

b) 最高价格为 3(第 5 天)所以我们继续在第 3 天和第 4 天买入股票并在第 5 天卖出(利润 = ( 3*2 - 3 = 3 )

c) 总利润 = 3 + 3 = 6

这个复杂度原来是 O(n^2) 。此解决方案通过了 11 个用例中的 10 个,但超过了最后一个测试用例(即最大输入)的时间限制

谁能想到一个更有效的解决方案来解决这个问题?有动态规划解决方案吗?

【问题讨论】:

我投票结束这个问题,因为它与编程无关。充其量只是一道数学题。 【参考方案1】:

我能够找到一个关于 O(n) 时间复杂度的简单解决方案。

下面是它的代码:


/** 
* 
* @author techieExpress 
* 
* The cost of a stock on each day is given in an array, 
* find the max profit that you can make by buying and selling in those days.  
* For example, if the given array is 100, 180, 260, 310, 40, 535, 695,  
* the maximum profit can earned by buying on day 0, selling on day 3.  
* Again buy on day 4 and sell on day 6.  
* If the given array of prices is sorted in decreasing order,  
* then profit cannot be earned at all. 
*  
* 
* YouTube video explanation link - https://youtu.be/IYENA3WpwsA 
**/ 
import java.util.ArrayList; 
//Solution structure 
class Interval  
    int buy, sell; 
 
public class stockBuySell  
    // This function finds the buy sell schedule for maximum profit 
    //  100,50, 180, 260, 310, 40, 535, 695  ,n=7 
    public void stockBuySell(int price[], int n)  
        // Prices must be given for at least two days 
        if (n < 2) 
            return; 
        int count = 0; 
        // solution array 
        ArrayList<Interval> sol = new ArrayList<Interval>(); 
        // Traverse through given price array 
        int i = 0; 
        while (i < n - 1)  
            // Find Local Minima. Note that the limit is (n-2) as we are 
            // comparing present element to the next element. 
            while ((i < n - 1) && (price[i ] >= price[i+1])) 
            i++; 
            // If we reached the end, break as no further solution possible 
            if (i == n - 1) 
                break; 
            Interval e = new Interval(); 
            // Store the index of minima 
            e.buy = i; 
            i++; 
            // Find Local Maxima. Note that the limit is (n-1) as we are 
            // comparing to previous element 
            while ((i < n) && (price[i] >= price[i - 1])) 
                i++; 
            // Store the index of maxima 
            e.sell = i - 1; 
            sol.add(e); 
            // Increment number of buy/sell 
            count++; 
         
        // print solution 
        if (count == 0) 
            System.out.println("There is no day when buying the stock " + "will make profit"); 
        else 
            for (int j = 0; j < count; j++) 
                System.out.println("Buy on day: " + sol.get(j).buy + " " + "Sell on day : " + sol.get(j).sell); 
        return; 
     
    public static void main(String args[])  
        // stock prices on consecutive days 
        int price[] =  100,50,130,140,40,20,200,30,10 ; 
        int n = price.length; 
        stockBuySell stock = new stockBuySell(); 
        stock.stockBuySell(price, n); 
     
 

代码的 Git 链接:https://github.com/TechieExpress/Data..。

如果你想了解所使用的底层概念,可以观看 TechieExpress youtube 频道视频的详细解释 - https://youtu.be/kA6O1laqrvY

【讨论】:

【参考方案2】:
private static int MaxProfit(int[] A)
        
            if (A.Length == 0)
                return 0;
            Stack<int> repositoryStack = new Stack<int>();
            int maxProfit = 0;
            int tempProfit;
            for (int i = 0; i < A.Length; i++)
            
                if (repositoryStack.Count == 0)
                
                    repositoryStack.Push(i);
                    continue;
                
                while (repositoryStack.Count != 0 && A[i] < A[repositoryStack.Peek()])
                
                    repositoryStack.Pop();
                
                if (repositoryStack.Count != 0 && A[i] > A[repositoryStack.Peek()])
                
                    tempProfit = A[i] - A[repositoryStack.Peek()];
                    if (tempProfit > maxProfit)
                        maxProfit = tempProfit;
                
                if(repositoryStack.Count == 0)
                    repositoryStack.Push(i);
            
            return maxProfit;
        

【讨论】:

【参考方案3】:

这里有更简单易懂的算法;

    private static void BuyOnceAndSellONce()
    
        int[] stock = new int[]  100, 180, 260, 310, 40, 535, 695 ;
        int profit = 0;
        int minimumPrice = int.MaxValue;
        for (int i = 0; i < stock.Length; i++)
        
            profit = Math.Max(profit, stock[i] - minimumPrice);
            minimumPrice = Math.Min(stock[i], minimumPrice);

        
        Console.WriteLine("profit "  + profit);
    

    private static void MultipleBuySellButNonOverlappingTransactions()
    
        int[] stock = new int[]  100, 180, 260, 310, 40, 535, 695 ;
        int totalProfit = 0;
        int currentProfit = 0;
        for (int i = 1; i < stock.Length;i++)
        
            currentProfit = stock[i] - stock[i - 1];
            if (currentProfit > 0)
                totalProfit += currentProfit;
        

        Console.WriteLine(totalProfit);
    

【讨论】:

【参考方案4】:

这个任务的另一个 O(n) 解决方案可以通过使用局部最小值和最大值来找到 max 和 min 之间的最佳偏差(利润),知道 max 应该比 min 具有更大的索引。我们还需要查看以前的最佳本地最小值(C# 实现)。

public int[] GetBestShareBuyingStrategy(int[] price)
    
        var n = price.Length;
        if (n <= 1)
            return null;

        var profit = 0;
        var min = 0;
        var max = 0;
        var lmin = 0;

        for (var i = 1; i < n; i++)
        
            var lmax = i;
            var lp = price[lmax] - price[lmin];
            if (lp <= 0)
            
                lmin = i;
            
            else
            
                var tp = price[lmax] - price[min];
                if (lp > tp && lp > profit)
                
                    min = lmin;
                    max = lmax;
                    profit = lp;
                
                else if (tp > profit)
                
                    max = lmax;
                    profit = tp;
                
            
        

        return profit > 0
            ? new [] min, max
            : null;
    



    [Test]
    [TestCase(new[]  10, 9, 8, 7, 3 )]
    [TestCase(new[]  5, 5, 5, 5, 5 )]
    [TestCase(new[]  5, 4, 4, 4 )]
    [TestCase(new[]  5, 5, 3, 3 )]
    public void GetBestShareBuyingStrategy_When_no_sense_to_buy(int[] sharePrices)
    
        var resultStrategy = GetBestShareBuyingStrategy(sharePrices);
        Assert.IsNull(resultStrategy);
    

    [Test]
    [TestCase(new[]  10, 8, 12, 20, 10 , 1, 3)]
    [TestCase(new[]  5, 8, 12, 20, 30 , 0, 4)]
    [TestCase(new[]  10, 8, 2, 20, 10 , 2, 3)]
    [TestCase(new[]  10, 8, 2, 20, 10 , 2, 3)]
    [TestCase(new[]  10, 2, 8, 1, 15, 20, 10, 22 , 3, 7)]
    [TestCase(new[]  1, 5, 2, 7, 3, 9, 8, 7 , 0, 5)]
    [TestCase(new[]  3, 5, 2, 7, 3, 9, 8, 7 , 2, 5)]
    public void GetBestShareBuyingStrategy_PositiveStrategy(int[] sharePrices, int buyOn, int sellOn)
    
        var resultStrategy = GetBestShareBuyingStrategy(sharePrices);
        Assert.AreEqual(buyOn, resultStrategy[0]);
        Assert.AreEqual(sellOn, resultStrategy[1]);
    

【讨论】:

【参考方案5】:

我同意您的方法的逻辑,但无需进行递归处理或全局最大值搜索。要查找卖出/买入天数,您只需每天查看一次:

诀窍是从头开始。 如果你能及时倒退,股票交易很容易!

如果您认为代码比文字更容易阅读,请跳过我的解释,但这里是:

从头到尾,看当天的价格。这是迄今为止(从最后)的最高价格,然后卖掉!最后一天(我们开始阅读的地方)你总是会卖出。

然后转到第二天(记住,时间倒流)。它是迄今为止最高的价格吗(从我们所看到的一切来看)? - 然后全部卖掉,你不会找到更好的一天。否则价格上涨,所以购买。以同样的方式继续直到开始。

一个反向循环解决了整个问题:计算交易的决策和利润。

这是 C 类 python 中的代码:(我避免使用大多数 Python 的东西。对于 C 人来说应该是可读的)

def calcprofit(stockvalues): 
    dobuy=[1]*len(stockvalues) # 1 for buy, 0 for sell
    prof=0
    m=0
    for i in reversed(range(len(stockvalues))):
        ai=stockvalues[i] # shorthand name
        if m<=ai:
            dobuy[i]=0
            m=ai
        prof+=m-ai
    return (prof,dobuy)  

例子:

calcprofit([1,3,1,2]) gives (3, [1, 0, 1, 0])
calcprofit([1,2,100]) gives (197, [1, 1, 0])
calcprofit([5,3,2])   gives (0, [0, 0, 0])
calcprofit([31,312,3,35,33,3,44,123,126,2,4,1]) gives
 (798, [1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0])

请注意,m 是我们所见过的最高股价(从最后)。如果ai==m,那么在该步骤买入的股票的利润为0:在那之后我们有下跌或稳定的价格并且没有买入。

您可以通过一个简单的循环来验证利润计算是否正确(为简单起见,假设它在上述函数中)

stock=0
money=0
for i in range(len(stockvalues)):  
    if dobuy[i]:
        stock+=1
        money-=stockvalues[i]
    else:
        money+=stockvalues[i]*stock
        stock=0
print("profit was: ",money)

【讨论】:

理论上,这个问题列在“动态规划”下,这里与动态规划有什么关系?如果您能解释这种关系,我将不胜感激。 'calcprofit([1,2,100]) 给出 (197, [1, 1, 0])' : 有点好奇利润怎么可能是 197 @craftsmannadeem:先以 1 美元的价格购买一只股票,然后以 2 美元的价格购买另一只股票,然后以每股 100 美元的价格出售这两只股票。 如果最后一个值最高,那么中间的所有交易机会都会被错过,例如calcprofit([1,2,3,1,2,3,1,2,3,4])(18, [1, 1, 1, 1, 1, 1, 1, 1, 1, 0]) @alex true,但发布的问题与这种策略无关:该问题有一个明确的限制,即您每天最多购买一只股票(也请查看原始问题示例)。我的回答正确地解决了相关问题,但正如您所说,有更好的策略。【参考方案6】:

你的逻辑是正确的......

以全局最大值出售..但不需要递归...

如果第 i 个元素是全局最大值...卖出 i 之前的所有股票!

现在问题简化为以前的答案+ i+1 到 N...

不需要递归......我们可以线性计算!

【讨论】:

【参考方案7】:

我的理由是,在最高股价之前买入的每只股票,您都会获利。使用这种思路,您可以在最高价之前买入每只股票,然后以最高价卖出,然后对剩余的股票价格重复同样的操作。

function profit(prices)
    var totalStocks = 0, profitMade = 0;

    var buySell = function(sortedPrices)
        for(var i = 0, len = sortedPrices.length; i < len; i++)
            if (i < len - 1)
                totalStocks++;
                profitMade = profitMade - sortedPrices[i];
            else
                profitMade = profitMade + totalStocks * sortedPrices[i];
                totalStocks = 0;
            
        
    , splitMaxPrice = function(rawPrices)
        var leftStocks = rawPrices.splice(rawPrices.lastIndexOf(Math.max.apply(null, rawPrices))+1);
        buySell(rawPrices);
        if(leftStocks.length > 0)
            splitMaxPrice(leftStocks);
        
        return;
    ;

    splitMaxPrice(prices);

    return profitMade;


【讨论】:

【参考方案8】:

0.从数组末尾开始,无需递归 1. smax = 列表中的最高股票价格 2.然后假设你已经买了所有的股票直到smax来找到利润 然后你以 smax 的价格卖掉它

          public static void main(String[] args) 

          Scanner sc = new Scanner(System.in);
          int numOfTestCase = sc.nextInt();
          for (int i = 0; i < numOfTestCase; i++) 
                 int n = sc.nextInt();
                 long profit = 0;
                 int[] stockPrice = new int[n];

                 for (int j = 0; j < n; j++) 
                       stockPrice[j] = sc.nextInt();
                 

                 int currMax = Integer.MIN_VALUE;

                 for (int j = n - 1; j >= 0; j--) 
                       if (currMax < stockPrice[j]) 
                              currMax = stockPrice[j];
                       
                       profit += (currMax - stockPrice[j]);
                 
                 System.out.println(profit);


          
   

【讨论】:

【参考方案9】:

我刚刚在一个竞赛网站上解决了这个问题。我想我得到的算法比公认的答案更简单。

1. smax = maximum stock price from the list
2. then find the profit by assuming you have bought all the stocks till smax 
   and you sell it at the price of smax
3. then check if smax is the last element of the stock price list 
   if yes then return profit as answer, 
   if no 
   then make a new list containing stock prices after smax to the last stock price
   and repeat steps 1-3 and keep adding profit of each iteration to get the final profit.

【讨论】:

【参考方案10】:

另一种看待它的方式:在预处理中,对于每个元素 a[i] 找到 a[j] s.t. j &gt; i 最大化(a[j] - a[i]) 因此,您可以以a[i] 的价格购买的最佳是在a[i] 购买和在a[j] 出售。如果不存在a[j] s.t. a[j] &gt; a[i] 然后a[i] 根本不是购买点。

预处理时间:O(N)

S[N-1] = A[N-1];
for(int i=N-2; i>=0; --i)
       S[i] = max(A[i], S[i+1]);

这里,S[i] 是你应该卖出 a[i] 的价格。

总结总利润:O(N)

long long int Profit = 0;
    for(int i=0;i<N;++i)
          Profit += max(0,  (S[i]-A[i]) );

【讨论】:

对于每个元素 a[i] 找到 a[j] s.t. j > i 这个 O(n) 怎么样?这不是 O(n^2) 吗? 我们可以在 O(N) 本身中处理这些信息。 (我已经更新了我的帖子。)我认为您的解决方案和我的解决方案相同。我只是想从一开始就将其形象化,仅此而已。 只解释你如何为每个 a[i] 找到 a[j] s.t. j > i 用于线性访问中的 i。 “现在也”是什么意思?我已经发布了预处理(获得每个点的最佳售价)可以在 O(N) 时间内完成。 在总结利润时是否需要 max() 函数? S[i] 总是大于或等于 A[i] 因此你总是得到 max(0,0) 或 (0, something_bigger_than_0)。正如您所指出的,它与 Johan 的解决方案完全相同,但需要额外的 O(n) 空间。

以上是关于最大化给定股票报价的利润[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

2021-07-05:股票问题2。给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖

2021-07-07:股票问题4。给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成(

2021-07-08:股票问题5。给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票)

352 买卖股票的good时机和最大子数组(todo,动态规划)

2021-07-06:股票问题3。给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。注意:你不能同时参与多笔交易(

maxProfit