给定股票报价最大化利润

时间:2012-03-01 10:06:15

标签: algorithm recursion dynamic-programming

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

Code Sprint:systems

**问题:

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

示例(输入,即天数不同)

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

1 2 100 =>利润= 197

1 3 1 2 =>利润= 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个但超过了最后一个测试案例的时间限制(即最大的输入)

所以我的问题是,任何人都可以想到一个更有效的解决方案吗?是否有动态编程解决方案?

P.S:这是我第一次在这里提问。所以如果我需要改进/添加这个问题,请告诉我

10 个答案:

答案 0 :(得分:57)

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

诀窍是从头开始。 如果您的旅行倒退,股票交易很容易!

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

从最后阅读,看看当天的价格。这是迄今为止的最高价格(从结束),然后卖!最后一天(我们开始阅读)你将永远卖出。

然后去第二天(记住,及时倒退)。它是迄今为止的最高价格(从我们所看到的所有)? - 然后卖掉所有,你将找不到更美好的一天。否则价格上涨,所以买。继续以同样的方式直到开始。

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

这是C-like python中的代码:(我避免了大多数pythonic的东西。对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)

答案 1 :(得分:6)

另一种观察方式:
在预处理中,对于每个元素a[i],找到a[j] s.t. j > i并最大化(a[j] - a[i])
因此,您可以a[i]的价格购买 最佳 ,价格为a[i],卖a[j]。如果没有a[j] s.t. a[j] > 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]是您应该卖出[i]的价格。

总结利润总额:O(N)

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

答案 2 :(得分:4)

此任务的另一个O(n)解决方案可以通过使用局部最小值和最大值来找到最大值和最小值之间的最佳差值(利润),知道max应该具有更大的索引然后min。我们还需要查看以前最好的本地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]);
    }

答案 3 :(得分:3)

0.从数组末尾开始,因此无需递归
1. smax =清单中的最高股票价格 然后假设你已经买了所有的股票直到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);


          }
   }

答案 4 :(得分:2)

我刚刚在比赛网站上解决了这个问题。我认为我得到的算法比接受的答案更简单。

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.

答案 5 :(得分:0)

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

在全球最大值出售..但不需要递归......

如果ith元素是全局最大值...在我之前卖掉所有股票!

现在问题减少到先前的回答+ i + 1到N ...

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

答案 6 :(得分:0)

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

    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);
    }

答案 7 :(得分:0)

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;
        }

答案 8 :(得分:0)

我能够找到 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

答案 9 :(得分:-1)

我的理由是,您为最高股价之前购买的每一只股票赚取利润。使用这种思路,您可以在最高价格之前购买每一只股票,最大限度地卖出它,并对剩余的股票价格重复同样的事情。

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;

}