web-dev-qa-db-fra.com

Maximiser le profit pour les cours boursiers donnés

On m'a posé cette question lors d'une interview pour une startup et je l'ai encore vu lors du récent concours de 

Code Sprint: systèmes

**La question :

Vous êtes donné les prix des actions pour une série de jours. Chaque jour, vous pouvez acheter une unité d’actions, vendre un nombre illimité d’actions que vous avez déjà achetées ou ne rien faire. Quel est le profit maximum que vous pouvez obtenir en planifiant de manière optimale votre stratégie de trading? **

Exemples (L'entrée c.-à-d. Le nombre de jours peut varier)

5 3 2 => profit = 0 // puisque le prix diminue chaque jour, le profit maximum que nous pouvons réaliser = 0 

1 2 100 => profit = 197 

1 3 1 2 => profit = 3 // on achète à 1 on vend à 3, puis on achète à 1 et on vend à 2 ..total profit = 3 

Ma solution: 

a) Trouve le jour où le cours de l'action était le plus élevé. Continuez à acheter 1 unité de stock jusqu'à ce jour. 

b) Si ce jour est le dernier jour, alors quittez: 

autre: Vendez tous les stocks ce jour-là et divisez le tableau après ce jour et répétez sur les éléments restants
c) fusionner les bénéfices 

par exemple 1 4 1 2 3
a) prix de l'action le plus élevé au jour 2 .. nous achetons donc des actions le jour 1 et les vendons le jour 2 (bénéfice = 3), puis nous récifions les jours suivants: 1 2 3

b) Le prix maximum est 3 (jour 5), nous continuons donc à acheter des actions les jours 3 et 4 et vendons le jour 5 (profit = (3 * 2 - 3 = 3) 

c) Gain total = 3 + 3 = 6 

La complexité pour cela s'avère être O (n ^ 2). cette solution a réussi 10 des 11 cas mais a dépassé le délai imparti pour un dernier cas test (c'est-à-dire l'entrée la plus importante) 

Ma question est donc la suivante: est-ce que quelqu'un peut penser à une solution plus efficace à ce problème? Existe-t-il une solution de programmation dynamique? 

P.S: c'est la première fois que je pose une question ici. alors s'il vous plaît laissez-moi savoir si je dois améliorer/ajouter des choses à cette question 

38
thor_hayek

Je suis d'accord avec la logique de votre méthode mais il n'y a pas besoin de faire de traitement récursif ni de recherche de maxima globaux. Pour trouver les jours de vente/d'achat, il vous suffit de regarder chaque jour une fois:

L'astuce consiste à partir de la fin. Le commerce des actions est facile si votre voyage en arrière dans le temps!

Si vous pensez que le code est plus facile à lire que les mots, ignorez simplement mon explication, mais voici:

En lisant à la fin, regardez le prix de cette journée. Est-ce le prix le plus élevé jusqu'ici (de la fin), alors vendez! Le dernier jour (où nous commençons à lire), vous vendez toujours. 

Ensuite, passez au lendemain (souvenez-vous, en arrière dans le temps). Est-ce le prix le plus élevé jusqu'ici (de tout ce que nous avons vu jusqu'à présent)? - Alors vendez tout, vous ne trouverez pas de meilleur jour. Sinon, les prix augmentent, alors achetez. continuez de la même manière jusqu'au début. 

Tout le problème est résolu avec une seule boucle inverse : calculant à la fois les décisions et le profit du commerce.

Voici le code en python de type C: (J'ai évité la plupart des choses Pythonic. Devrait être lisible par une personne en 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)  

Exemples:

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])

Notez que m est le cours le plus élevé que nous ayons vu (à la fin). Si ai==m, le profit sur les actions achetées à l'étape est égal à 0: nous avons eu un prix décroissant ou stable après ce point et nous n'avons pas acheté.

Vous pouvez vérifier que le calcul du bénéfice est correct avec une simple boucle (pour simplifier, imaginez que cela se trouve dans la fonction ci-dessus)

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)
55
Johan Lundberg

Une autre façon de voir les choses: 
En pré-traitement, pour chaque élément a[i] trouvez a[j] s.t. j > i et il maximise (a[j] - a[i]) 
Donc, leMeilleurque vous pouvez faire pour un prix à a[i] est Acheter à a[i] et Vendre à a[j] S'il n'existe pas de a[j] s.t. a[j] > a[i] alors a[i] n'est pas du tout un point d'achat.

Temps de prétraitement: O(N) 

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

Ici, S [i] est le prix auquel vous devriez vendre un [i]. 

Résumer le bénéfice total: O(N)

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

Une autre solution O(n) pour cette tâche peut être faite en utilisant les minimum et maximum locaux pour trouver la meilleure retenue entre (max) et min, sachant que max doit avoir un indice supérieur à min. Nous devons également examiner le meilleur précédent local (implémentation de 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]);
    }
4
Vasyl Zvarydchuk

0.Start à partir de la fin du tableau de sorte qu'il n'est pas nécessaire de recurse
1. smax = cours maximum de la liste
2.Puis trouvez le profit en supposant que vous avez acheté tous les stocks jusqu’à smax et vous le vendez au prix de 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);


          }
   }
3
Tokala Sai Teja

Je viens de résoudre ce problème dans un site de concours. Je pense avoir un algorithme plus simple que la réponse acceptée.

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.
2
coding_pleasures

voici plus simple et facile à comprendre algo;

    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);
    }
0
AvtarSohi
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;
        }
0
Mohamed.Emad

ta logique est correcte ...

vendre aux maxima globaux..mais la récursion n'est pas nécessaire ...

si cet élément est un maximum global ... vendez tous les stocks avant moi!

Maintenant le problème se réduit à la réponse précédente + i + 1 à N ...

la récursivité n'est pas nécessaire ... nous pouvons calculer linéairement!

0
Shivendra