最大单卖利润

时间:2011-08-16 23:45:55

标签: arrays algorithm big-o time-complexity

假设我们得到一个 n 整数数组,表示一天的股票价格。我们希望找到一对(buyDay,sellDay)buyDay≤soldDay,这样如果我们在 buyDay 上购买该股票并将其出售在 sellDay 上,我们会最大化利润。

显然,通过尝试所有可能的(buyDay,sellDay)对并采取该算法的 O(n 2 解决方案所有这些中最好的。但是,是否有更好的算法,也许是在 O(n)时间运行的算法?

20 个答案:

答案 0 :(得分:275)

答案 1 :(得分:33)

这是带有一点间接的最大和子序列问题。最大和子序列问题给出一个整数列表,可以是正数或负数,找到该列表的连续子集的最大总和。

您可以通过在连续几天之间获取利润或亏损来轻松地将此问题转换为该问题。因此,您将转换股票价格列表,例如[5, 6, 7, 4, 2]列入收益/损失列表,例如[1, 1, -3, -2]。然后,子序列和问题很容易解决:Find the subsequence with largest sum of elements in an array

答案 2 :(得分:15)

我不确定为什么这被认为是动态编程问题。我在教科书和算法指南中看到了这个问题,使用O(n log n)运行时和O(log n)表示空间(例如编程访谈元素)。这似乎是一个比人们正在做的更简单的问题。

这可以通过跟踪最大利润,最低购买价格以及最终买入/卖出价格来实现。当它遍历数组中的每个元素时,它会检查给定元素是否小于最低购买价格。如果是,则将最小购买价格指数(min)更新为该元素的索引。此外,对于每个元素,becomeABillionaire算法会检查arr[i] - arr[min](当前元素和最小购买价格之间的差异)是否大于当前利润。如果是,则将利润更新为该差异,并将买入设置为arr[min],并将卖出设置为arr[i]

一次完成。

static void becomeABillionaire(int arr[]) {
    int i = 0, buy = 0, sell = 0, min = 0, profit = 0;

    for (i = 0; i < arr.length; i++) {
        if (arr[i] < arr[min])
            min = i;
        else if (arr[i] - arr[min] > profit) {
            buy = min; 
            sell = i;
            profit = arr[i] - arr[min];
        }

    }

    System.out.println("We will buy at : " + arr[buy] + " sell at " + arr[sell] + 
            " and become billionaires worth " + profit );

}

合着者:https://stackoverflow.com/users/599402/ephraim

答案 3 :(得分:2)

问题与最大子序列相同 我用动态编程解决了它。跟踪当前和之前的(利润,推荐和销售日期) 如果电流高于先前电流,则用电流替换前一电流。

    int prices[] = { 38, 37, 35, 31, 20, 24, 35, 21, 24, 21, 23, 20, 23, 25, 27 };

    int buyDate = 0, tempbuyDate = 0;
    int sellDate = 0, tempsellDate = 0; 

    int profit = 0, tempProfit =0;
    int i ,x = prices.length;
    int previousDayPrice = prices[0], currentDayprice=0;

    for(i=1 ; i<x; i++ ) {

        currentDayprice = prices[i];

        if(currentDayprice > previousDayPrice ) {  // price went up

            tempProfit = tempProfit + currentDayprice - previousDayPrice;
            tempsellDate = i;
        }
        else { // price went down 

            if(tempProfit>profit) { // check if the current Profit is higher than previous profit....

                profit = tempProfit;
                sellDate = tempsellDate;
                buyDate = tempbuyDate;
            } 
                                     // re-intialized buy&sell date, profit....
                tempsellDate = i;
                tempbuyDate = i;
                tempProfit =0;
        }
        previousDayPrice = currentDayprice;
    }

    // if the profit is highest till the last date....
    if(tempProfit>profit) {
        System.out.println("buydate " + tempbuyDate + " selldate " + tempsellDate + " profit " + tempProfit );
    }
    else {
        System.out.println("buydate " + buyDate + " selldate " + sellDate + " profit " + profit );
    }   

答案 4 :(得分:2)

这是我的Java解决方案:

public static void main(String[] args) {
    int A[] = {5,10,4,6,12};

    int min = A[0]; // Lets assume first element is minimum
    int maxProfit = 0; // 0 profit, if we buy & sell on same day.
    int profit = 0;
    int minIndex = 0; // Index of buy date
    int maxIndex = 0; // Index of sell date

    //Run the loop from next element
    for (int i = 1; i < A.length; i++) {
        //Keep track of minimum buy price & index
        if (A[i] < min) {
            min = A[i];
            minIndex = i;
        }
        profit = A[i] - min;
        //If new profit is more than previous profit, keep it and update the max index
        if (profit > maxProfit) {
            maxProfit = profit;
            maxIndex = i;
        }
    }
    System.out.println("maxProfit is "+maxProfit);
    System.out.println("minIndex is "+minIndex);
    System.out.println("maxIndex is "+maxIndex);     
}

答案 5 :(得分:1)

这是一个有趣的问题,因为似乎很难,但仔细考虑会产生一个优雅,精简的解决方案。

如前所述,它可以在O(N ^ 2)时间内解决蛮力。对于数组(或列表)中的每个条目,迭代所有先前的条目以获得最小值或最大值,具体取决于问题是找到最大的收益还是损失。

以下是如何考虑O(N)中的解决方案:每个条目代表一个新的可能最大值(或最小值)。然后,我们需要做的就是保存前一个min(或max),并将diff与当前和前一个min(或max)进行比较。容易腻。

以下是Java中作为JUnit测试的代码:

import org.junit.Test;

public class MaxDiffOverSeriesProblem {

    @Test
    public void test1() {
        int[] testArr = new int[]{100, 80, 70, 65, 95, 120, 150, 75, 95, 100, 110, 120, 90, 80, 85, 90};

        System.out.println("maxLoss: " + calculateMaxLossOverSeries(testArr) + ", maxGain: " + calculateMaxGainOverSeries(testArr));
    }

    private int calculateMaxLossOverSeries(int[] arr) {
        int maxLoss = 0;

        int idxMax = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > arr[idxMax]) {
                idxMax = i;
            }

            if (arr[idxMax] - arr[i] > maxLoss) {
                maxLoss = arr[idxMax] - arr[i];
            }           
        }

        return maxLoss;
    }

    private int calculateMaxGainOverSeries(int[] arr) {
        int maxGain = 0;

        int idxMin = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < arr[idxMin]) {
                idxMin = i;
            }

            if (arr[i] - arr[idxMin] > maxGain) {
                maxGain = arr[i] - arr[idxMin];
            }           
        }

        return maxGain;
    }

}

在计算最大损失的情况下,我们会跟踪列表中的最大值(买入价格),直至当前条目。然后我们计算最大和当前条目之间的差异。如果max - current&gt; maxLoss,然后我们将这个差异保留为新的maxLoss。由于保证最大指数小于当前指数,我们保证“买入”日期小于“卖出”日期。

在计算最大增益的情况下,一切都是相反的。我们跟踪列表中的最小值直到当前条目。我们计算最小和当前条目之间的差异(反转减法中的顺序)。如果current - min> maxGain,然后我们将这个差异保持为新的maxGain。同样,'买入'(分钟)的指数出现在当前指数('卖出')之前。

我们只需要跟踪maxGain(或maxLoss)以及min或max的索引,但不能同时跟踪两者,我们不需要比较索引来验证'buy'是否小于'sell'因为我们自然而然地得到了这个。

答案 6 :(得分:1)

最高投票的答案不允许最大利润为负的情况,并且应该修改以允许此类情况。可以通过将循环的范围限制为(len(a)-1)并通过将索引移动一来改变利润的方式来实现。

def singSellProfit(a):
profit = -max(a)
low = a[0]

for i in range(len(a) - 1):
    low = min(low, a[i])
    profit = max(profit, a[i + 1] - low)
return profit

将此版本的函数与数组的前一版本进行比较:

s = [19,11,10,8,5,2]

singSellProfit(s)
-1

DynamicProgrammingSingleSellProfit(s)
0

答案 7 :(得分:1)

最高单一销售利润,O(n)解决方案

function stocks_n(price_list){
    var maxDif=0, min=price_list[0]

    for (var i in price_list){
        p = price_list[i];
        if (p<min)
            min=p
        else if (p-min>maxDif)
                maxDif=p-min;
   }

    return maxDif
}

这是一个项目,对100k整数的随机数据集上的o(N)vs o(n ^ 2)方法进行时间复杂度测试。 O(n ^ 2)需要2秒,而O(n)需要0.01秒

https://github.com/gulakov/complexity.js

function stocks_n2(ps){
    for (maxDif=0,i=_i=0;p=ps[i++];i=_i++)
        for (;p2=ps[i++];)
            if (p2-p>maxDif)
                maxDif=p2-p
    return maxDif
}

这是一种较慢的o(n ^ 2)方法,它在每天的剩余时间内循环,双循环。

答案 8 :(得分:1)

public static double maxProfit(double [] stockPrices)
    {
        double initIndex = 0, finalIndex = 0;

        double tempProfit = list[1] - list[0];
        double maxSum = tempProfit;
        double maxEndPoint = tempProfit;


        for(int i = 1 ;i<list.length;i++)
        {
            tempProfit = list[ i ] - list[i - 1];;

            if(maxEndPoint < 0)
            {
                maxEndPoint = tempProfit;
                initIndex = i;
            }
            else
            {
                maxEndPoint += tempProfit;
            }

            if(maxSum <= maxEndPoint)
            {
                maxSum = maxEndPoint ;
                finalIndex = i;
            }
        }
        System.out.println(initIndex + " " + finalIndex);
        return maxSum;

    }

这是我的解决方案。修改最大子序列算法。解决了O(n)中的问题。我认为不能更快地完成。

答案 9 :(得分:1)

我提出了一个简单的解决方案 - 代码更多是不言自明的。这是动态编程问题之一。

代码没有处理错误检查和边缘情况。它只是一个样本,提供了解决问题的基本逻辑。

namespace MaxProfitForSharePrice
{
    class MaxProfitForSharePrice
    {
        private static int findMax(int a, int b)
        {
            return a > b ? a : b;
        }

        private static void GetMaxProfit(int[] sharePrices)
        {
            int minSharePrice = sharePrices[0], maxSharePrice = 0, MaxProft = 0;
            int shareBuyValue = sharePrices[0], shareSellValue = sharePrices[0];

            for (int i = 0; i < sharePrices.Length; i++)
            {
                if (sharePrices[i] < minSharePrice )
                {
                    minSharePrice = sharePrices[i];
                    // if we update the min value of share, we need to reset the Max value as 
                    // we can only do this transaction in-sequence. We need to buy first and then only we can sell.
                    maxSharePrice = 0; 
                }
                else 
                {
                    maxSharePrice = sharePrices[i];
                }

                // We are checking if max and min share value of stock are going to
                // give us better profit compare to the previously stored one, then store those share values.
                if (MaxProft < (maxSharePrice - minSharePrice))
                {
                    shareBuyValue = minSharePrice;
                    shareSellValue = maxSharePrice;
                }

                MaxProft = findMax(MaxProft, maxSharePrice - minSharePrice);
            }

            Console.WriteLine("Buy stock at ${0} and sell at ${1}, maximum profit can be earned ${2}.", shareBuyValue, shareSellValue, MaxProft);
        }

        static void Main(string[] args)
        {
           int[] sampleArray = new int[] { 1, 3, 4, 1, 1, 2, 11 };
           GetMaxProfit(sampleArray);
            Console.ReadLine();
        }
    }
}

答案 10 :(得分:0)

确定最大利润的可能性可能是跟踪阵列中每个索引处阵列中的左侧最小和最右侧最大元素。当您在迭代股票价格时,在任何特定日期,您将知道截至当天的最低价格,并且您还将知道当天(包括)当天的最高价格。

例如,让我们定义一个min_arrmax_arr,其中给定的数组为arri中的索引min_arr将是arr中所有索引<= i(左侧和包括i)的最小元素。 i中的索引max_arr将是arr中所有索引>= i(i的权利,包括i)的最大元素。然后,您可以找到max_arr和“min_arr&#39;”中相应元素之间的最大差异:

def max_profit(arr)
   min_arr = []
   min_el = arr.first
   arr.each do |el|
       if el < min_el
           min_el = el
           min_arr << min_el
       else
           min_arr << min_el
       end
   end

   max_arr = []
   max_el = arr.last
   arr.reverse.each do |el|
       if el > max_el
           max_el = el
           max_arr.unshift(max_el)
       else
           max_arr.unshift(max_el)
       end

   end

   max_difference = max_arr.first - min_arr.first
   1.upto(arr.length-1) do |i|
        max_difference = max_arr[i] - min_arr[i] if max_difference < max_arr[i] - min_arr[i]  
   end

   return max_difference 
end

这应该在O(n)时间运行,但我相信它占用了大量空间。

答案 11 :(得分:0)

这是我的解决方案

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText);
}
}
xhr.open('GET', 'url', true);
xhr.send(null); 

答案 12 :(得分:0)

static void findmaxprofit(int[] stockvalues){
    int buy=0,sell=0,buyingpoint=0,sellingpoint=0,profit=0,currentprofit=0;
    int finalbuy=0,finalsell=0;
    if(stockvalues.length!=0){
        buy=stockvalues[0];
    }           
    for(int i=1;i<stockvalues.length;i++){  
        if(stockvalues[i]<buy&&i!=stockvalues.length-1){                
            buy=stockvalues[i];
            buyingpoint=i;
        }               
        else if(stockvalues[i]>buy){                
            sell=stockvalues[i];
            sellingpoint=i;
        }
        currentprofit=sell-buy;         
        if(profit<currentprofit&&sellingpoint>buyingpoint){             
            finalbuy=buy;
            finalsell=sell;
            profit=currentprofit;
        }

    }
    if(profit>0)
    System.out.println("Buy shares at "+finalbuy+" INR and Sell Shares "+finalsell+" INR and Profit of "+profit+" INR");
    else
        System.out.println("Don't do Share transacations today");
}

答案 13 :(得分:0)

这是数组中两个元素之间的最大差异,这是我的解决方案:

O(N)时间复杂度 O(1)空间复杂度

    int[] arr   =   {5, 4, 6 ,7 ,6 ,3 ,2, 5};

    int start   =   0;
    int end     =   0;
    int max     =   0;
    for(int i=1; i<arr.length; i++){
        int currMax =   arr[i] - arr[i-1];
        if(currMax>0){
            if((arr[i] -arr[start])>=currMax && ((arr[i] -arr[start])>=(arr[end] -arr[start]))){

                 end    =   i;
            }
            else if(currMax>(arr[i] -arr[start]) && currMax >(arr[end] - arr[start])){
                start   =   i-1;
                end =   i;
            }
        }
    }
    max =   arr[end] - arr[start];
    System.out.println("max: "+max+" start: "+start+" end: "+end);

答案 14 :(得分:0)

在FB解决方案工程师职位的现场编码考试中失败后,我必须在平静的气氛中解决它,所以这是我的2美分:

var max_profit = 0;
var stockPrices = [23,40,21,67,1,50,22,38,2,62];

var currentBestBuy = 0; 
var currentBestSell = 0;
var min = 0;

for(var i = 0;i < (stockPrices.length - 1) ; i++){
    if(( stockPrices[i + 1] - stockPrices[currentBestBuy] > max_profit) ){
        max_profit = stockPrices[i + 1] - stockPrices[currentBestBuy];
        currentBestSell = i + 1;  
    }
    if(stockPrices[i] < stockPrices[currentBestBuy]){
            min = i;
        }
    if( max_profit < stockPrices[i + 1] - stockPrices[min] ){
        max_profit = stockPrices[i + 1] - stockPrices[min];
        currentBestSell = i + 1;
        currentBestBuy = min;
    }
}

console.log(currentBestBuy);
console.log(currentBestSell);
console.log(max_profit);

答案 15 :(得分:0)

真正回答这个问题的唯一答案是@akash_magoon(以这么简单的方式!),但它不会返回问题中指定的确切对象。我重构了一点,并在PHP中回答了我的回答:

function maximizeProfit(array $dailyPrices)
{
    $buyDay = $sellDay = $cheaperDay = $profit = 0;

    for ($today = 0; $today < count($dailyPrices); $today++) {
        if ($dailyPrices[$today] < $dailyPrices[$cheaperDay]) {
            $cheaperDay = $today;
        } elseif ($dailyPrices[$today] - $dailyPrices[$cheaperDay] > $profit) {
            $buyDay  = $cheaperDay;
            $sellDay = $today;
            $profit   = $dailyPrices[$today] - $dailyPrices[$cheaperDay];
        }
    }
    return [$buyDay, $sellDay];
}

答案 16 :(得分:0)

一个简洁的解决方案:

public void newUser(String name ,String username ,String password ,String email)
  {
    User newUser = new User(name, username, password, email);
    newUser.addUser();
    database.add(newUser);
  }

答案 17 :(得分:0)

def get_max_profit(stock):
    p=stock[0]
    max_profit=0
    maxp=p
    minp=p
    for i in range(1,len(stock)):
        p=min(p,stock[i])
        profit=stock[i]-p
        if profit>max_profit:
            maxp=stock[i]
            minp=p
            max_profit=profit
    return minp,maxp,max_profit



stock_prices = [310,315,275,295,260,270,290,230,255,250]
print(get_max_profit(stock_prices))

python3中的该程序可以返回将最大化利润的买入价和卖出价,这些价是用 O(n)的时间复杂度和O(1)的空间复杂度计算的。 strong>。

答案 18 :(得分:0)

对于多次买入/卖出, 使用下面的代码

  • 时间复杂度 O(n)

    n=int(input())
    a = list(map(int,input().split())) 
    m=0
    b=a[0]
    s=0
    for i in range(1,n):
      if(a[i]<a[i-1]):
        s=a[i-1]
        m=m+s-b
        b=a[i]
    if(a[n-1]>b):
      m=m+a[n-1]-b
    print(m)
    

答案 19 :(得分:-1)

对于跟踪最小和最大元素的所有答案,该解决方案实际上是O(n ^ 2)解决方案。这是因为最后必须检查最大值是否在最小值之后发生。如果没有,则需要进一步迭代,直到满足该条件,并且这留下了最坏情况的O(n ^ 2)。如果你想跳过额外的迭代,那么需要更多的空间。无论哪种方式,与动态编程解决方案相比都是禁忌