reset password
Author Message
cpham24
Posts: 6
Posted 00:48 Mar 25, 2018 |

I thought long and hard about it and with the hints given (only 4 things need to be kept track of, and there should be only one loop), I came up with this:

public static int[] findBuySell(int[] a) {
        int n = a.length;
        int buy = -1;
        int sell = -1;
        int profit = 0;
        int pot_buy = 0;
        
        for(int i=1; i<n; i++) {
            if(a[i] < a[pot_buy])
                pot_buy = i;
            else if(a[i] - a[pot_buy] >= profit) {
                buy = pot_buy;
                sell = i;
                profit = a[i] - a[pot_buy];
            }
        }
        
        return new int[]{buy, sell};
    }

So... my thought process is fairly straight forward with this one: have an initial "buying candidate" (potential buy, or pot_buy) that might turn out to be my next point of purchase (not decided yet). Initially, profit is set to 0 (so no profit). If we can find an index with which the difference against pot_buy is greater than the old profit, then we set profit to be that new difference, and we set sell and buy accordingly. If there is an index at which a value is lower than pot_buy, then set pot_buy to that, or otherwise, pot_buy will just be at the current "buy" value and then we'll just continue to evaluate all other indices to see if a greater difference (profit) can be found.

I don't know if this solution is "right" or not (I haven't thought about which test case it might fail), but it seems more intuitive and "optimized" than what I tried to do with the left/right indices. This just came to me while I was thinking about the problem on the plane, and I thought of putting it down right away.

cpham24
Posts: 6
Posted 10:49 Mar 25, 2018 |

Never mind. I just realized that Dr. Abbott posted a Netlogo model for this. Haha. Upon inspection, the mechanism works somewhat similar to my code.

So this turns out to be quite right after all (interesting).

Yeah, this was very simple when I thought about it, but for some reason, it didn't come to mind immediately. Glad to see that it's the right-ish solution. Also if done this way, I guess keeping track of "profit" may not be necessary, even, so maybe this is the optimized solution:

public static int[] findBuySell(int[] a) {
        int buy = 0;
        int sell = 0;
        int smallest = 0;
        
        for(int i=1; i<a.length; i++) {
            if(a[i] < a[smallest])
                smallest = i;
            else if(a[i] - a[smallest] >= a[sell] - a[buy]) {
                buy = smallest;
                sell = i;
            }
        }
        
        return new int[]{buy, sell}; // returns [0, 0] if no greater profit can be found (so we sell immediately after we buy?)
    }

rabbott
Posts: 1649
Posted 11:25 Mar 25, 2018 |

Very good. That's essentially what I do, although I keep track of 5 named values:

previous_best_buy_index
previous_best_sell_index
tentative_buy_index 
current_index
tentative_sell_index.

As you discovered, the trick is to realize that when the current index drops below the tentative_buy_index, the amount of profit from the tentative_buy_index to the tentative_sell_index can never increase. That's because if we encounter a higher tentative_sell_index after dropping below the tentative_buy_index, we do better by buying at the new lower index, which becomes the new tentative_buy_index. So at that point the question is whether the newly "closed" tentative_buy_index --> tentative_sell_index profit  is better than the previous best buy-sell indices. Whichever is better becomes (or stays) the previous_best_buy(sell)_index.

It's interesting how difficult it is to explain this in a few words even though it's not all that complex. 

Last edited by rabbott at 12:58 Mar 25, 2018.
cpham24
Posts: 6
Posted 11:53 Mar 25, 2018 |

Yeah! I find it really weird that the solution just came very... intuitively, like I could have simply verbalized a description based on that and it would have been the solution. Something like...

"Initially, assume we'll buy at the first value. Then for each successive value (iteration), look at whether we'll make a greater profit when the selling price is higher, then sell at that point. If a selling price is lower, maybe that's the better time to buy, so keep track of that and see if there will be greater profit to be found after that point, then set the buying and selling points accordingly."

I can't even imagine why I didn't just think of that to begin with. The left/right indices as well as sub-max-array (modified to fit this problem so not exactly sub-max anymore?) candidate solutions (is it even possible to find any solution using these methods?) seem way too complicated and complex for this.

Thanks, Dr. Abbott! This was a very good brain teaser!

rabbott
Posts: 1649
Posted 13:25 Mar 25, 2018 |

I'm going to try again in English.

The basic idea is that we fix a tentative buy index. Then we scan to the right looking for (and keeping track of) the highest sell index encountered. (Simple and obvious so far.)

If as we're scanning to the right we find an index whose value is less than the tentative buy index, we know what the maximum profit from that tentative buy index is. (It is the difference between the tentative buy index and the highest sell index found to that point.)

Why is that the maximum possible profit? Because if we subsequently find a higher sell index we would do better by buying at the current index, which is lower than the tentative buy index, and selling at that higher sell index than if we bought at the tentative buy index and sold at the higher sell index. 

So we call the pair consisting of the current tentative buy index and current tentative sell index closed.

We want to keep track of that closed pair just in case there is no better result from buying at the new lower index. So we call it the previous best closed buy and sell indices.  

Since this may happen multiple times, before committing to the newly closed pair as the best so far, we have to compare it to the previous best closed buy and sell index pair if any. We then keep the better one and discard the other.

So at this point we know we must keep track of

(a) the best closed buy and sell pair.encountered so far 

(b) the current lowest buy index encountered so far. (We don't yet know what the maximum profit for that buy index is.)

(c) the best sell index so far encountered for the current buy index. 

As described above, we continue to scan to the right looking for either a higher sell index or a lower buy index.

When we reach the end of the array we compare the current buy-sell index with the previous best closed buy-sell index and return the better of the two.

That's still a lot of words.

rabbott
Posts: 1649
Posted 20:59 Mar 25, 2018 |

Once again in English.

We scan from left to right looking for successively lower buy points. For each such buy point we scan to the right looking for successively higher sell points. For a given buy point we stop looking for sell points whenever we find a lower buy point. Thus each new lower buy point fixes the preceding buy point's maximum gain. We keep track of the (buy point, sell point) pair with the largest gain.

This time a few words do the job reasonably well!

Last edited by rabbott at 21:00 Mar 25, 2018.