Monday, March 6, 2017

Max Profit on Stocks Daily

Writing programming interview questions hasn't made me rich. Maybe trading Apple stocks will.
Suppose we could access yesterday's stock prices as a list, where:
  • The values are the price in dollars of Apple stock.
  • A higher index indicates a later time.
So if the stock cost $500 at 10:30am and $550 at 11:00am, then:
stock_prices_yesterday[60] = 500
Write an efficient function that takes stock_prices_yesterday and returns the best profit I could have made from 1 purchase and 1 sale of 1 Apple stock yesterday.
For example:
  stock_prices_yesterday = [10, 7, 5, 8, 11, 9]

get_max_profit(stock_prices_yesterday)
# returns 6 (buying for $5 and selling for $11)
No "shorting"—you must buy before you sell. You may not buy and sell in the same time step (at least 1 minute must pass).

Gotchas

It is not sufficient to simply take the difference between the highest price and the lowest price, because the highest price may come before the lowest price. You must buy before you sell.
What if the stock value goes down all day? In that case, the best profit will be negative.
You can do this in O(n) time and O(1) space!

Breakdown

To start, try writing an example value for stock_prices_yesterday and finding the maximum profit "by hand." What's your process for figuring out the maximum profit?
The brute force ↴ approach would be to try every pair of times (treating the earlier time as the buy time and the later time as the sell time) and see which one is higher.
  def get_max_profit(stock_prices_yesterday):

    max_profit = 0

    # go through every time
    for outer_time in xrange(len(stock_prices_yesterday)):

        # for every time, go through every OTHER time
        for inner_time in xrange(len(stock_prices_yesterday)):

            # for each pair, find the earlier and later times
            earlier_time = min(outer_time, inner_time)
            later_time   = max(outer_time, inner_time)

            # and use those to find the earlier and later prices
            earlier_price = stock_prices_yesterday[earlier_time]
            later_price   = stock_prices_yesterday[later_time]

            # see what our profit would be if we bought at the
            # earlier price and sold at the later price
            potential_profit = later_price - earlier_price

            # update max_profit if we can do better
            max_profit = max(max_profit, potential_profit)

    return max_profit
But that will take O(n^2) time, since we have two nested loops—for every time, we're going through every other time. Also, it's not correct: we won't ever report a negative profit! Can we do better?
Well, we’re doing a lot of extra work. We’re looking at every pair twice. We know we have to buy before we sell, so in our inner for loop we could just look at every price after the price in our outer for loop.
That could look like this:
  def get_max_profit(stock_prices_yesterday):

    max_profit = 0

    # go through every price (with its index as the time)
    for earlier_time, earlier_price in enumerate(stock_prices_yesterday):

        # and go through all the LATER prices
        for later_price in stock_prices_yesterday[earlier_time+1:]:

            # see what our profit would be if we bought at the
            # earlier price and sold at the later price
            potential_profit = later_price - earlier_price

            # update max_profit if we can do better
            max_profit = max(max_profit, potential_profit)

    return max_profit
What’s our runtime now?
Well, our outer for loop goes through all the times and prices, but our inner for loop goes through one fewer price each time. So our total number of steps is the sum n + (n - 1) + (n - 2) ... + 2 + 1 ↴ , which is still O(n^2) time.
We can do better!
If we're going to do better than O(n^2), we're probably going to do it in either O(n\lg{n}) or O(n). O(n\lg{n}) comes up in sorting and searching algorithms where we're recursively cutting the set in half. It's not obvious that we can save time by cutting the set in half here. Let's first see how well we can do by looping through the set only once.
Since we're trying to loop through the set once, let's use a greedy ↴ approach, where we keep a running max_profit until we reach the end. We'll start our max_profit at $0. As we're iterating, how do we know if we've found a new max_profit?
At each iteration, our max_profit is either:
  1. the same as the max_profit at the last time step, or
  2. the max profit we can get by selling at the current_price
How do we know when we have case (2)?
The max profit we can get by selling at the current_price is simply the difference between the current_price and the min_price from earlier in the day. If this difference is greater than the current max_profit, we have a new max_profit.
So for every price, we’ll need to:
  • keep track of the lowest price we’ve seen so far
  • see if we can get a better profit
Here’s one possible solution:
  def get_max_profit(stock_prices_yesterday):

    min_price = stock_prices_yesterday[0]
    max_profit = 0

    for current_price in stock_prices_yesterday:

        # ensure min_price is the lowest price we've seen so far
        min_price = min(min_price, current_price)

        # see what our profit would be if we bought at the
        # min price and sold at the current price
        potential_profit = current_price - min_price

        # update max_profit if we can do better
        max_profit = max(max_profit, potential_profit)

    return max_profit
We’re finding the max profit with one pass and constant space!
Are we done? Let’s think about some edge cases. What if the stock value stays the same? What if the stock value goes down all day?
If the stock price doesn't change, the max possible profit is 0. Our function will correctly return that. So we're good.
But if the value goes down all day, we’re in trouble. Our function would return 0, but there’s no way we could break even if the price always goes down.
How can we handle this?
Well, what are our options? Leaving our function as it is and just returning zero is not a reasonable option—we wouldn't know if our best profit was negative or actually zero, so we'd be losing information. Two reasonable options could be:
  1. return a negative profit. “What’s the least badly we could have done?”
  2. raise an exception. “We should not have purchased stocks yesterday!”
In this case, it’s probably best to go with option (1). The advantages of returning a negative profit are:
  • We more accurately answer the challenge. If profit is "revenue minus expenses", we’re returning the best we could have done.
  • It's less opinionated. We'll leave decisions up to our function’s users. It would be easy to wrap our function in a helper function to decide if it’s worth making a purchase.
  • We allow ourselves to collect better data. It matters if we would have lost money, and it matters how much we would have lost. If we’re trying to get rich, we’ll probably care about those numbers.
How can we adjust our function to return a negative profit if we can only lose money?Initializing max_profit to 0 won’t work...
Well, we started our min_price at the first price, so let’s start our max_profit at the first profit we could get—if we buy at the first time and sell at the second time.
  min_price = stock_prices_yesterday[0]
max_profit = stock_prices_yesterday[1] - stock_prices_yesterday[0]
But we have the potential for an index out of bounds error here, if stock_prices_yesterday has fewer than 2 prices.
We do want to raise an exception in that case, since profit requires buying and selling, which we can't do with less than 2 prices. So, let's explicitly check for this case and handle it:
  if len(stock_prices_yesterday) < 2:
    raise IndexError('Getting a profit requires at least 2 prices')

min_price = stock_prices_yesterday[0]
max_profit = stock_prices_yesterday[1] - stock_prices_yesterday[0]

# etc...
Ok, does that work?
No! max_profit is still always 0! What’s happening?
If the price always goes down, min_price is always set to the current_price. So current_price - min_price comes out to 0, which of course will always be greater than a negative profit.
When we’re calculating the max_profit, we need to make sure we never have a case where we try both buying and selling stocks at the current_price.
To make sure we’re always buying at an earlier price, never the current_price, let’s switch the order around so we calculate max_profit before we update min_price.
We'll also need to pay special attention to time 0. Make sure we don't try to buy and sell at time 0!

Solution

We’ll greedily ↴ walk through the list to track the max profit and lowest price so far.
For every price, we check if:
  • we can get a better profit by buying at min_price and selling at the current_price
  • we have a new min_price
To start, we initialize:
  1. min_price as the first price of the day
  2. max_profit as the first profit we could get
We decided to return a negative profit if the price decreases all day and we can't make any money. We could have raised an exception instead, but returning the negative profit is cleaner, makes our function less opinionated, and ensures we don't lose information.
  def get_max_profit(stock_prices_yesterday):

    # make sure we have at least 2 prices
    if len(stock_prices_yesterday) < 2:
        raise IndexError('Getting a profit requires at least 2 prices')

    # we'll greedily update min_price and max_profit, so we initialize
    # them to the first price and the first possible profit
    min_price = stock_prices_yesterday[0]
    max_profit = stock_prices_yesterday[1] - stock_prices_yesterday[0]

    for index, current_price in enumerate(stock_prices_yesterday):

        # skip the first (0th) time
        # we can't sell at the first time, since we must buy first,
        # and we can't buy and sell at the same time!
        # if we took this out, we'd try to buy *and* sell at time 0.
        # this would give a profit of 0, which is a problem if our
        # max_profit is supposed to be *negative*--we'd return 0!
        if index == 0:
            continue

        # see what our profit would be if we bought at the
        # min price and sold at the current price
        potential_profit = current_price - min_price

        # update max_profit if we can do better
        max_profit = max(max_profit, potential_profit)

        # update min_price so it's always
        # the lowest price we've seen so far
        min_price  = min(min_price, current_price)

    return max_profit

Complexity

O(n) time and O(1) space. We only loop through the list once.

What We Learned

This one's a good example of the greedy ↴ approach in action. Greedy approaches are great because they're fast (usually just one pass through the input). But they don't work for every problem.
How do you know if a problem will lend itself to a greedy approach? Best bet is to try it out and see if it works. Trying out a greedy approach should be one of the first ways you try to break down a new question.
To try it on a new problem, start by asking yourself:
"Suppose we could come up with the answer in one pass through the input, by simply updating the 'best answer so far' as we went. What additional values would we need to keep updated as we looked at each item in our set, in order to be able to update the 'best answer so far' in constant time?"
In this case:
The "best answer so far" is, of course, the max profit that we can get based on the prices we've seen so far.
The "additional value" is the minimum price we've seen so far. If we keep that updated, we can use it to calculate the new max profit so far in constant time. The max profit is the larger of:
  1. The previous max profit
  2. The max profit we can get by selling now (the current price minus the minimum price seen so far)
Try applying this greedy methodology to future questions.

Two Eggs Problem

A building has 100 floors. One of the floors is the highest floor an egg can be dropped from without breaking.
If an egg is dropped from above that floor, it will break. If it is dropped from that floor or below, it will be completely undamaged and you can drop the egg again.
Given two eggs, find the highest floor an egg can be dropped from without breaking, with as few drops as possible.

Gotchas

We can do better than a binary approach, which would have a worst case of 50 drops.
We can even do better than 19 drops!
We can always find the highest floor an egg can be dropped from with a worst case of 14 total drops.

Breakdown

What if we only had one egg? How could we find the correct floor?
Because we can't use the egg again if it breaks, we'd have to play it safe and drop the egg from every floor, starting at the bottom and working our way up. In the worst case, the egg won't break until the top floor, so we'd drop the egg 100 times.
What does having two eggs allow us to do differently?
Since we have two eggs, we can skip multiple floors at a time until the first egg breaks, keeping track of which floors we dropped it from. Once that egg breaks we can use the second egg to try every floor, starting on the last floor where the egg didn't break and ending on the floor below the one where it did break.
How should we choose how many floors to skip with the first egg?
What about trying a binary ↴ approach? We could drop the first egg halfway up the building at the 50th floor. If the egg doesn't break, we can try the 75th floor next. We keep going like this, dividing the problem in half at each step. As soon as the first egg breaks, we can start using our second egg on our (now-hopefully narrow) range of possible floors.
If we do that, what's the worst case number of total drops?
The worst case is that the highest floor an egg won't break from is floor 48 or 49. We'd drop the first egg from the 50th floor, and then we'd have to drop the second egg from every floor from 1 to 49, for a total of 50 drops. (Even if the highest floor an egg won't break from is floor 48, we still won't know if it will break from floor 49 until we try.)
Can we do better than this binary approach?
50 is probably too many floors to skip for the first drop. In the worst case, if the first egg breaks after a small number of drops, the second egg will break after a large number of drops. And if we went the other way and skipped 1 floor every time, we'd have the opposite problem! What would the worst case floor be then?
The worst case would be floor 98 or 99—the first egg would drop a large number of times (at every floor from 2-100 skipping one floor each time) and the last egg would drop a small number of times (only on floor 99), for a total of 51 drops.
Can we balance this out? Is there some number between 50 and 1—the number of floors we'll skip with each drop of the first egg—where the first and second eggs would drop close to the samenumber of times in the worst case?
Yes, we could skip 10 floors each time. The worst case would again be floor 98 or 99, but we'd only drop the first egg 10 times and the second egg 9 times, for a total of 19 drops!
Is that the best we can do?
Let's look at what happens with this strategy each time the first egg doesn't break. How does the worst case total number of drops change?
The worst case total number of drops increases by one each time the first egg doesn't break
For example, if the egg breaks on our first drop from the 10th floor, we may have to drop the second egg at each floor between 1 and 9 for a worst case of 10 total drops. But if the egg breaks when we skip to the 20th floor we will have a worst case of 11 total drops (once for the 10th floor, once for the 20th, and all of the floors between 11 and 19)!
How can we keep the worst case number of drops from increasing each time the first egg doesn't break?
Since the maximum number of drops increases by one each time we skip the same amount of floors, we could skip one fewer floor each time we drop the first egg!
But how do we choose how many floors to skip the first time?
Well, we know two things that can help us:
  1. We want to skip as few floors as possible the first time we drop an egg, so if our first egg breaks right away we don't have a lot of floors to drop our second egg from.
  2. We always want to be able to reduce the number of floors we're skipping by one. We don't want to get towards the top and not be able to skip any floors any more.
Now that we know this, can we figure out the ideal number of floors to skip the first time?
To be able to decrease the number of floors we skip by one every time we move up, and to minimize the number of floors we skip the first time, we want to end up skipping just one floor at the very top. Can we model this with an equation?
Let's say n is the number of floors we'll skip the first time, and we know 1 is the number of floors we'll skip last. Our equation will be:
n + (n-1) + (n-2) + \ldots + 1 = 100
How can we solve for n? Notice that we're summing every number from 1 to n.
The left side is a triangular series ↴ . Knowing this, can we simplify and solve the equation?
We know the left side reduces to:
\frac{n^2 + n}{2}
so we can plug that in:
\frac{n^2 + n}{2}=100
and we can rearrange to get a quadratic equation:
n^2 + n - 200 = 0
which gives us 13.651.
We can't skip a fraction of a floor, so how many floors should we skip the first time? And what's our final worst case total number of drops?

Solution

We'll use the first egg to get a range of possible floors that contain the highest floor an egg can be dropped from without breaking. To do this, we'll drop it from increasingly higher floors until it breaks, skipping some number of floors each time.
When the first egg breaks, we'll use the second egg to find the exact highest floor an egg can be dropped from. We only have to drop the second egg starting from the last floor where the first egg didn't break, up to the floor where the first egg did break. But we have to drop the second egg one floor at a time.
With the first egg, if we skip the same number of floors every time, the worst case number of drops will increase by one every time the first egg doesn't break. To counter this and keep our worst case drops constant, we decrease the number of floors we skip by one each time we drop the first egg.
When we're choosing how many floors to skip the first time we drop the first egg, we know:
  1. We want to skip as few floors as possible, so if the first egg breaks right away we don't have a lot of floors to drop our second egg from.
  2. We always want to be able to reduce the number of floors we're skipping. We don't want to get towards the top and not be able to skip floors any more.
Knowing this, we set up the following equation where n is the number of floors we skip the first time:
n + (n-1) + (n-2) + \ldots + 1 = 100
This triangular series ↴ reduces to n * (n+1) / 2 = 100 which solves to give n = 13.651. We round up to 14 to be safe. So our first drop will be from the 14th floor, our second will be 13 floors higher on the 27th floor and so on until the first egg breaks. Once it breaks, we'll use the second egg to try every floor starting with the last floor where the first egg didn't break. At worst, we'll drop both eggs a combined total of 14 times.
For example:
  Highest floor an egg won't break from
13

Floors we drop first egg from
14

Floors we drop second egg from
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Total number of drops
14
  Highest floor an egg won't break from
98

Floors we drop first egg from
14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99

Floors we drop second egg from
96, 97, 98

Total number of drops
14

What We Learned

This is one of our most contentious questions. Some people say, "Ugh, this is useless as an interview question," while others say, "We ask this at my company, it works great."
The bottom line is some companies do ask questions like this, so it's worth being prepared. There are a bunch of these not-exactly-programming interview questions that lean on math and logic. There are some famous ones about shuffling cards and rolling dice. If math isn't your strong suit, don't fret. It only takes a few practice problems to get the hang of these.

Saturday, February 4, 2017

AngularJS Services

$rootScope
$scope
$http
$q
$templateCache
$compile
$timeout
$sce
$filter
$window
$stateParams
$injector


_

Wednesday, January 18, 2017

Agile Development - Handling Bugs

Handling Bugs

My Point of View 

Within a Sprint:
It is important to complete the team's acceptable level of testing during the sprint and before Sprint Review. From this, bugs from the current sprint will be need to addressed. Here is the recommended way to do so:
  • If the bug is related to an acceptance criteria not being met, fix it during the sprint.
  • If the bug is due to a gap that was not anticipated (business gap), get with the Scrum Master/Product Owner and discuss the bug to prioritize its resolution. This may result in the creation of new story instead of a defect.
  • If the bug is reported too close to the Demo, evaluate the severity with the entire Scrum Team. If it is a show-stopper then raise it as a Blocking issue.
Post Sprint (Discovered in Sprint Review)
  • If the bug is easy/quick to fix (one liner, etc), then just fix it.
  • If the bug is not trivial, and not a blocker, then add it to the backlog.
  • If the bug is a blocker then add a task to the current story in the current sprint to capture the work required to fix it, and start working on it. This may require that another story be moved from the current sprint to the backlog to account for the new hours. (The Development Team only has so many hours available within a sprint. See: Capacity Planning)
References
http://agileatlas.org/articles/item/how-to-handle-defects-in-scrum
http://www.infoq.com/news/2009/07/coping-with-bugs

http://www.mountaingoatsoftware.com/blog/should-story-points-be-assigned-to-a-bug-fixing-story