Найдите цены покупки / продажи в массиве значений акций, чтобы максимизировать положительную разницу

получил этот вопрос в интервью Сегодня, и его оптимизированное решение остановило меня (что дует, потому что я действительно хотел работать в этой компании...)

учитывая один массив реальных значений, каждый из которых представляет стоимость акций компании после произвольного периода времени, найдите лучшую цену покупки и соответствующую ей лучшую цену продажи (buy low, sell high).

чтобы проиллюстрировать на примере, возьмем акции компании Z:

55.39 109.23 48.29 81.59 105.53 94.45 12.24

важно отметить тот факт, что массив "сортируется" во времени, т. е. с течением времени значения добавляются в правый конец массива. Таким образом, наша стоимость покупки будет (должна быть) слева от нашей стоимости продажи.

(в приведенном выше примере идеальным решением является покупка по адресу 48.29 и продать в 105.53)

Я придумал наивное решение достаточно легко с O (n2) сложность (реализуется в java):

// returns a 2-element array: first element is the index in the argument array
// of the best buying price, and the second element is the index of the best
// selling price which, collectively, maximize the trading return
//
// if there is no favorable trading (e.g. prices monotonically fall), null is returned
public int[] maximizeReturn(ArrayList<Double> prices) {
  int [] retval = new int[2];
  int BUY = 0, SELL = 1;
  retval[BUY] = retval[SELL] = -1; // indices of buy and sell prices, respectively

  for (int i = 0; i < prices.size(); i++) {
    for (int j = i + 1; j < prices.size(); j++) {
      double difference = prices.get(j).doubleValue() - 
                          prices.get(i).doubleValue();

      if (difference > 0.0) {
        if (retval[BUY] < 0 || difference > prices.get(retval[SELL]).doubleValue() - 
                                            prices.get(retval[BUY]).doubleValue()) {
          retval[BUY] = i;
          retval[SELL] = j;
        }
      }
    }
  }
  return (retval[BUY] > 0 ? retval : null);
}

вот где я облажался: есть линейное время o(n) и решением, и я полностью бомбил, пытаясь понять это (да, я знаю, потерпеть неудачу). Кто-нибудь знает, как реализовать решение линейного времени? (любой язык, который вам удобен) спасибо!

редактировать

Я полагаю, для всех заинтересованных, я только что получил сообщение сегодня, что я не получил работу, для которой я интервьюировал, где они задали мне этот вопрос. :(

15 ответов


вот попытка (C++). В основном каждый раз, когда я отслеживаю новый топ, я пытаюсь увидеть, является ли это лучшей прибылью thusfar. Я знаю, что" дно " должно было быть обнаружено раньше. В этот момент я вспоминаю верхнюю, нижнюю и текущую максимальную прибыль. Если новое дно обнаружено позже, его после текущего верха, поэтому мы должны сбросить верх и посмотреть, может ли немного более низкий "верх" дать лучшую прибыль.

#include <iostream>

int main()
{

    double REALLY_BIG_NO = 1e99;
    double bottom = REALLY_BIG_NO; // arbirtrary large number
    double currBestBuy = 0.0;
    double top = 0.0;
    double currBestSell = 0.0;
    double profit = 0.0;

    // array of prices
    double prices[] = {10.50, 55.39, 109.23, 48.29, 81.59, 105.53, 94.45, 12.24, 152.0, 2, 170.0};
    int numPrices = 10;// number of prices

    for (int i = 0; i < numPrices; ++i)
    {
         if (prices[i] < bottom)
         {
            bottom = prices[i];
            // reset the search on a new bottom
            top = 0.0;
         }
         else if (prices[i] > top)
         {
            top = prices[i];
           // calculate profit
            double potentialProfit = (top - bottom);
            if (potentialProfit > profit &&
                bottom != REALLY_BIG_NO)
            {
                profit = potentialProfit;
                currBestSell = top;
                currBestBuy = bottom;
            }
         }
    }

    std::cout << "Best Buy: " << currBestBuy << "Best Sell: " << currBestSell << std::endl;
}

до сих пор я играл с кучей разных входных наборов, и до сих пор я не было никаких проблем... (дайте мне знать, если вы проверите это и увидите что-то не так)

Я настоятельно рекомендую использовать Остин Салоненаобновленный ответ на этот вопрос и адаптация его к вашему языку.


В C#:

static void Main(string[] args)
{
    double[] values = new double[7]{55.39, 109.23, 48.29, 81.59, 105.53, 94.45, 12.24};

    double max = double.MinValue, maxDiff = double.MinValue, diff = 0;

    for (int i = 1; i < values.Length; i++)
    {
        if (values[i] > values[i - 1])
        {
            //trending upward, append to existing differential
            diff += values[i] - values[i - 1];
        }
        else
        {
            //trending downward, reset the diff
            diff = 0;
        }

        if (diff > maxDiff)
        {
            maxDiff = diff;
            max = values[i];
        }
    }

    Console.WriteLine("Buy at {0}; Sell at {1}", max - maxDiff, max);
}

редактировать: новый algo на основе неудачного тестового случая @Joe's-хороший улов кстати! Это также тот же ответ, что и @Doug T сейчас...

static void Main(string[] args)
{
    double[] values = new double[8] { 55.39, 109.23, 48.29, 81.59, 81.58, 105.53, 94.45, 12.24 };

    double max = double.MinValue, maxDiff = double.MinValue, diff = 0;
    double bottom = values[0];

    for (int i = 1; i < values.Length; i++)
    {
        diff += values[i] - values[i - 1];

        if (diff > maxDiff)
        {
            maxDiff = diff;
            max = values[i];
        }

        if (values[i] < bottom)
        {
            bottom = values[i];
            diff = 0;
        }
    }

    Console.WriteLine("Buy at {0}; Sell at {1}", max - maxDiff, max);
}

идея проста. Держите два указателя, lo и hi.
Сделайте петлю Foor

  1. если цена выше hi, обновите hi = price, continue
  2. если цена ниже, чем привет. Тогда lo и hi-один из возможных кандидатов. Рассчитайте прибыль, сохраните ее, если она больше предыдущей прибыли и сбросьте lo, hi to price

def getBestProfit(prices):
    lo = hi = profit = 0

    for price in prices:
        if lo == 0 and hi == 0:
            lo = hi = price

        if price > hi:
            hi = price

        if price < low:
            tmpProfit = hi - lo
            if tmpProfit > profit:
                profit = tmpProfit

            lo = hi = price
    return profit

вот это


void getBestTime (int stocks[], int sz, int &buy, int &sell){
int min = 0;
int maxDiff = 0;
buy = sell = 0;
for (int i = 0; i < sz; i++) 
{
    if (stocks[i] < stocks[min])
    {
        min = i;
    }
    int diff = stocks[i] - stocks[min];
    if (diff > maxDiff) 
    {
        buy = min;
        sell = i;
        maxDiff = diff;
    }
}}

на всякий случай, если вы предпочитаете этот ответ. Я нашел его в другой паутине, но все же. источник:http://leetcode.com/2010/11/best-time-to-buy-and-sell-stock.html


      public void profit(float stock[], int arlen ){
            float buy = stock[0];
            float sell = stock[arlen-1];
            int bi = 0;
            int si = arlen - 1;

            for( int i = 0; i < arlen && bi < si ; i++){

                    if( stock[i] <  buy && i < si){
                            buy = stock[i];
                            bi = i;
                    }
                    if(stock[arlen - i - 1] > sell &&  (arlen - i -1)  > bi){
                            sell = stock[arlen - i - 1];
                            si = arlen - i - 1;
                    }
            }
            System.out.println(buy+" "+sell);
    }

Я действительно должен указать, как вопрос интервью, ожидая, что вы решите его, поскольку O (n) граничит с абсурдом. Вопросы интервью призваны доказать, что вы можете решить проблему, которую вы смогли решить. Тот факт, что вы решили его в O(N^2) vs O(N), не имеет значения. Если компания пройдет мимо найма вас за то, что вы не решаете это в O(N), это, вероятно, не компания, в которой вы все равно хотели бы работать.


Я хотел бы описать, как я подошел к этой проблеме, чтобы было легче понять мой код:

(1) за каждый день, если бы я должен был продать свои акции в этот день, какова была бы минимальная сумма, которую я мог бы заплатить, чтобы купить его? По сути, я отслеживаю минимальную цену до этого дня

(2) за каждый день, если бы я продавал в этот день, сколько я зарабатываю? (Цена акций в этот день-минимальная цена)

Это показывает, что я должен отслеживать два вещи: (1) минимальная цена акций до сих пор (2) лучший заработок до сих пор.

проблема заключается в выборе дня продажи. Я продам в тот день, который принесет мне лучший доход. Вот мой Java-код:

    public static void findBestDeal(double [] stocks) {
    double minsofar = stocks[0];
    double bestsofar = 0.0;

    for(int i=1; i< stocks.length; i++) {

        // What is the cheapest price to buy it if I'm going to sell it today
        if(stocks[i-1] < minsofar) {
            minsofar = stocks[i-1];
        }

        // How much do I earn if I sell it on ith day?
        double current_deal = stocks[i] - minsofar;

        // Is selling today better?
        if(current_deal > bestsofar) {
            bestsofar = current_deal;
        }
    }

    System.out.println("Found the best deal: " + bestsofar + " (Bought at " + minsofar + " and sold at " + (minsofar+bestsofar) + ")");

}

вот моя реализация O(n) для этого. Я использую массив изменений для расчета максимальной прибыли и дат покупки и продажи. Ваши комментарии по этому поводу приветствуются.

#include<stdafx.h>
#include<stdio.h>

int main()
{
    //int arr[10] = {15, 3, 5,9,10,1,6,4,7,2};
    int arr[7] = {55.39, 109.23, 48.29, 81.59, 105.53, 94.45, 12.24};
    int change[7];
    int n=7;
    for(int i=1;i<=n;i++)
    {
    change[i] = arr[i]- arr[i-1];
    }
    int i=0,index = 0;
    int sum = 0;
    int maxsum = 0;
    int startpos = 0;
    int endpos = 0;
    while(index < n)
    {
        sum = sum + change[index];
        if(maxsum < sum)
        {
        maxsum = sum; 
        startpos = i;
        endpos = index;

        }
        else if (sum<0) // negative number ,set sum to zero
        {
        sum = 0;
        i=index+1;
        }
        index++;
    }

    printf("max profit is%d %d %d", maxsum , startpos, endpos+1 );
}

в моих усилиях научиться идти, а также сгрести мой мозг на этом, вот моя попытка.

func GetMaxProfit2(prices []float64) (float64, float64) {
    var min, max, pmin, pmax int

    for i, v := range prices {
        if v - prices[min] > prices[max] - prices[min] {
            pmax = max
            max = i
        }
        // Reset the max when min is updated.
        if v < prices[min] {
            pmin = min
            min = i
            pmax = max
            max = i
        }
    }

    // If min is ahead of max, reset the values back    
    if min >= max {
        min = pmin
        max = pmax
    }

    return prices[min], prices[max]
}

вот моя попытка использовать Javascript. Скрипт вычисляет ответ в O (N):

//Main Stock Array
var stock = [15, 20, 0, 3, 30, 45, 67, 92, 1, 4, 99];


//Setup initial variable state
var ans = {}, tmp = {}; //These are just for namespacing / syntatic sugar
ans.minVal = stock[0];
ans.minInd = 0;
ans.maxDiff = stock[1] - stock[0];
ans.maxInd = 1;
tmp.minInd = ans.minInd;
tmp.minVal = ans.minVal;

//Basically we iterate throught the array. If we find a new low, we start tracking it. Otherwise we compare the current index against the previously found low
for(i = 1; i <= stock.length-1; i++) {
    if(tmp.minVal > stock[i]) {
        tmp.minVal = stock[i];
        tmp.minInd = i;
    } else {
        ans.diff = stock[i] - stock[tmp.minInd];
        if(ans.diff > ans.maxDiff) { //Looks like we found a new maxDifference. Lets log the indexes
            ans.maxDiff = ans.diff;
            ans.maxInd = i;
            ans.minInd = tmp.minInd;
            ans.minVal = tmp.minVal;
        }
    }
}

document.write('You should buy your stocks on day ' + ans.minInd + ' and sell on day ' + ans.maxInd);

это решение C, которое действительно работает:

void bestBuySell() { двойные цены[] = {10.50, 10.0, 3.0, 194.0, 55.39, 2.0, 109.23, 48.29, 81.59, 105.53, 94.45, 191.0, 200.0, 12.24}; int arrSize = 14; double bestBuy = цены[0], bestSell = цены[1], bestPotentialBuy = цены[0]; double potentialProfit = цены[1] - цены[0];

for(int i = 1; i < (arrSize-1); i++)
{
    if(prices[i] < bestBuy)
        bestPotentialBuy = prices[i];            

    if((prices[i+1] - bestPotentialBuy) > potentialProfit)
    {
        bestBuy = bestPotentialBuy;
        bestSell = prices[i+1];
        potentialProfit = prices[i+1] - bestPotentialBuy;
    }
}

printf( "bestBuy %f bestSell %f\n", bestBuy, bestSell );

}


1.Мы не можем просто взять наименьшую сумму среди значений как " лучшая покупка "и максимальную сумму как" лучшая продажа", потому что" продажа "должна произойти после"покупки".

2.Мы не должны рассматривать записанный минимум как" лучшую покупку", потому что последующие дни могут иметь значения акций, разница которых с записанным минимумом может принести прибыль, которая может быть меньше, чем"записанная прибыль".

3.Best Buy и Best Sell рассматриваются как один вариант, потому что это положительный разница между этими значениями, что делает max profit.

4.Поскольку любой зарегистрированный минимум в прошлом является потенциальным кандидатом на покупку, условие максимальной прибыли всегда должно быть проверено на соответствие записанному минимуму и цене акций текущего дня.Поэтому мы всегда должны отслеживать записанный минимум, но просто наличие записанного минимума не является "лучшей покупкой" по причине № 2.

теперь имейте приведенный ниже код, который выполняется в O (n) раз, сделает чувство.

public class BestStockBuyAndSell {

public static void main(String[] args) {

    double[] stockPrices = {55.39,109.23,48.29,81.59,105.53,94.45,12.24};
    int [] bestBuySellIndex = maxProfit(stockPrices);

    System.out.println("Best Buy At "+stockPrices[bestBuySellIndex[0]]);
    System.out.println("Best Sell At "+stockPrices[bestBuySellIndex[1]]);

    System.out.println("Max Profit = "+(stockPrices[bestBuySellIndex[1]]-stockPrices[bestBuySellIndex[0]]));

}

public static int[] maxProfit(double[] stockPrices)
{
    int bestBuy=0;
    int bestSell=0;

    int[] bestCombination ={bestBuy,bestSell};
    double recordedMinimum = stockPrices[bestBuy];
    int recordedMinimuIndex = bestBuy;
    double bestProfitSofar = stockPrices[bestSell] - stockPrices[bestBuy];

    for(int i=1;i<stockPrices.length;i++)
    {
        if(stockPrices[i] - recordedMinimum > bestProfitSofar)
        {

            bestProfitSofar = stockPrices[i] - recordedMinimum;
            bestSell = i;
            bestBuy = recordedMinimuIndex;
        }

        if(stockPrices[i] < recordedMinimum)
        {
            recordedMinimuIndex = i;
            recordedMinimum = stockPrices[i];
        }

    }

    bestCombination[0] = bestBuy;
    bestCombination[1] = bestSell;


    return bestCombination;

}

}


Я придумал следующий алгоритм для этой проблемы, кажется, работает для всех входов. Кроме того, если стоимость акций продолжает падать, программа выведет не покупать этот запас:

  public class GetMaxProfit 
  { 

  double minValue = -1, maxValue = -1;
  double maxDiff = 0;

  public void getProfit(double [] inputArray){
    int i=0, j=1;
    double newDiff = 0;
    while(j<inputArray.length){
         newDiff = inputArray[j]-inputArray[i];
         if(newDiff > 0){
             if(newDiff > this.maxDiff){
               this.minValue = inputArray[i];
               this.maxValue = inputArray[j];
               this.maxDiff = newDiff;
             }
        }
        else{
            i = j;
        }
        j++;
    }
 }

 public static void main(String[] args) {
    // TODO Auto-generated method stub
    GetMaxProfit obj = new GetMaxProfit();

    obj.getProfit(new double[]{55.39, 19.23, 14.29, 11.59, 10.53, 9.45, 1.24});
    if(obj.minValue != -1 && obj.maxValue != -1){
      System.out.println("Buy Value for the input: "+obj.minValue);
      System.out.println("Sell Value for the input: "+obj.maxValue);
      System.out.println("Best profit for the input: "+obj.maxDiff);
            }
            else
               System.out.println("Do Not Buy This STOCK!!);

 }

}

есть ли какой-либо улов, который вы могли бы найти в этом? Сложность времени-O (N)


вот мое решение, такое же, как @Doug T. за исключением того, что я также отслеживаю день в индексе. Пожалуйста, предоставьте обратную связь.

 int prices[] = {4,4,5,6,2,5,1,1};
 //int prices[] = {100, 180, 260, 310, 40, 535, 695};

 int currentBestSellPrice=0;
 int currentBestBuyPrice=0;
 int lowindex=0;
 int highindex=0;
 int low=prices[0];
 int high=prices[0];
 int profit=0;
 int templowindex=0;
 for(int i=0; i< prices.length;i++)
 {
     // buy low
     if(prices[i] < low && i+1 < prices.length)
     {
         low = prices[i];  
         templowindex=i;
         high=0;
     }
     // sell high
     else if(prices[i] > high)
     {
         high = prices[i];
         int potentialprofit = (high-low);
         if(potentialprofit > profit)
         {
             profit = potentialprofit;
             currentBestSellPrice = high;
             currentBestBuyPrice = low;
             highindex=i;
             lowindex=templowindex;
         }
     }
 }


 System.out.println("Best Buy Price : "+ currentBestBuyPrice + " on day "+ lowindex);
 System.out.println("Best Sell Price : "+ currentBestSellPrice+ " on day "+ highindex );

F# решение для тех, кто заинтересован в функциональных взять на себя это. Я бы не сказал, что это сильно отличается.

let start, _, profit = 
    [55.39; 109.23; 48.29; 81.59; 81.58; 105.53; 94.45; 12.24 ]
    |> Seq.fold (fun (start,newStart,profit) i -> 
                    let start = defaultArg start i
                    let newStart = defaultArg newStart i
                    let newProfit = i - newStart
                    if profit < newProfit 
                    then  Some newStart, Some newStart,newProfit
                    else if start > i 
                    then Some start, Some i, profit 
                    else Some start,Some newStart,profit) (None,None, 0.0)
printf "Best buy: %f; Best sell: %f" start.Value (start.Value + profit)

выход:

Best buy: 48.290000; Best sell: 105.530000