Newsvendor problem

16 Aug 2016

Let's play a game. In the first period, the player buys some newspapers at 25 cents each (but they can't sell them yet). In the second period, the player sells some newspapers at 1 dollar each (but they can't buy more newspapers). Then the day ends, and any left-over demand or left-over newspapers become worthless.

If there are 3d6 customers, then the mean number of customers is 10.5. We can't certainly can't buy 10.5 newspapers. Should we buy 11 newspapers? On the one hand, that would be close to matching the expected demand. On the other hand, a newspaper only costs a quarter. Would it be reasonable to buy 12 newspapers, or even more?

Let's suppose that we commit to a policy, perhaps foolishly, of buying 11 newspapers. What would our profits be? Well, when 3d6 is greater than or equal to 11, we're limited by our stock, so our profit is 11 newspapers worth, which works out to... 11 * 1 (revenue) - 11 * 0.25 (cost) = 8.25 (profit). On the other hand, when 3d6 is less than 11, we're limited by the number of customers.

If we buy 11 newspapers, but only 3 people show up, then we only make one quarter of profit! Profit is c * 1 (revenue) - 11 * 0.25 (cost), if "c" is the number of customers.

Fooling around with the Troll dice calculator it seems like with this 11-newspapers policy, we would expect to make a profit of 27.166 quarters, or 27.166 / 4 = 6.7 dollars per day.

customers := sum 3d6;
papers := 11;
if customers < papers then 4 * customers - papers else 3 * papers

But it's easy to run the calculator with a different policies. Fooling around only a little more, plugging in different policies, we can find out that buying 12 newspapers per day has even better expected profit per day.

How would we compute the optimal policy? We might write a little code:

def profit(customers, papers):
  cost = 0.25 * papers
  if customers < papers:
    revenue = customers
  else:
    revenue = papers
  return revenue - cost

def expected_profit(papers):
  total = 0.0
  count = 0
  for i in range(1, 6):
    for j in range(1, 6):
      for k in range(1, 6):
        customers = i + j + k
        total += profit(customers, papers)
        count += 1
  return total / count

print '|papers bought|expected profit|'
print '|-------------|---------------|'
for papers in range(1, 36):
  print '|%d|%f|' % (papers, expected_profit(papers))

Which produces a reasonably nice table:

papers bought expected profit
1 0.750000
2 1.500000
3 2.250000
4 2.992000
5 3.710000
6 4.380000
7 4.970000
8 5.440000
9 5.766000
10 5.940000
11 5.970000
12 5.880000
13 5.710000
14 5.492000
15 5.250000
16 5.000000
17 4.750000
18 4.500000
19 4.250000
20 4.000000
21 3.750000
22 3.500000
23 3.250000
24 3.000000
25 2.750000
26 2.500000
27 2.250000
28 2.000000
29 1.750000
30 1.500000
31 1.250000
32 1.000000
33 0.750000
34 0.500000
35 0.250000

Stepping back a bit, one way you might turn this little, unfun game into a bigger (though perhaps still unfun) game is to consider a sequence of newsvendor games, and have the player graduate from one level to the next periodically (when they have demonstrated a certain level of facility, for example).

For example, what would the best policy be if the papers cost 2 dollars and sold for 7 dollars?

What if the distribution of customers was different?