Enigmatic Code

Programming Enigma Puzzles

Enigma 1156: The tax process

From New Scientist #2312, 13th October 2001 [link]

The people on the island of Fairshare have their own tax process. If A is the average income for the people on the island then only people earning more than A pay tax. If a person earns P, which is more than A, then that person pays tax (P−A)²/P.

When all the tax has been collected, then it is shared between the people earning less than A, in proportion to the amounts their incomes fall short of A.

There are 10 people on the island, C, D, E, F, G, H, I, J, K and L. Their final incomes after the tax process were:

C = F£117,
D = F£112,
E = F£103-58,
F = F£90,
G = F£60-47,
H = F£53-52,
I = F£51-15,
J = F£46-57,
K = F£44-20,
L = F£41-99 (that is to say 41 Fairshare pounds and 99 pence, where there are 116 pence to the Fairshare pound).

What were the original incomes of E, H and K?

See also Enigma 1253.



2 responses to “Enigma 1156: The tax process

  1. Jim Randell 1 August 2016 at 8:26 am

    This Python program runs in 200ms.

    from fractions import Fraction as F
    from enigma import subsets, irange, join, printf
    # "after" amounts
    after = (
      103 + F(58, 116),
      60 + F(47, 116),
      53 + F(52, 116),
      51 + F(15, 116),
      46 + F(57, 116),
      44 + F(20, 116),
      41 + F(99, 116),
    # number of residents
    n = len(after)
    # since the money is redistributed the average "before" earnings
    # are the same as the average "after" amounts
    A = F(sum(after), n)
    # calculate "before" values
    # ts = tax payers
    def calculate(ts):
      # go though the payers and calculate the "before" amounts and total
      # tax collected
      tax = 0
      before = [None] * n
      for i in ts:
        before[i] = F(A * A, 2 * A - after[i])
        # "before" amounts for tax payers must be >= A
        if before[i] < A: return None
        tax += before[i] - after[i]
      # go through the recipients and calculate the sum of their "after"
      # amounts, less the total amount of tax raised, which gives us the
      # sum of their "before" amounts
      t = sum(after[i] for (i, b) in enumerate(before) if b is None) - tax
      # the total number of "shares" in tax to be distributed is...
      T = (n - len(ts)) * A - t
      # if T = tax then everyone would have an "after" income of A
      # and we cannot determine the "before" incomes
      if T == tax: return None
      # calculate the "before" amounts for the beneficiaries
      for (i, b) in enumerate(before):
        if b is not None: continue
        before[i] = F(T * after[i] - A * tax, T - tax)
        # "before" amounts for beneficiaries must be below average
        if before[i] < 0 or before[i] > A: return None
      return before
    def amount(x):
      (a, b) = divmod(x, 1)
      return str(a) + '-' + str(int(116 * b))
    # choose the tax payers
    for ts in subsets(irange(0, n - 1)):
      before = calculate(ts)
      if before is None: continue
      printf("ts={ts}, before=[{before}]", before=join((amount(x) for x in before), sep=", "))

    Solution: The original income for E was F£128. The original income for H was F£32. The original income for K was F£12.

    The full list of incomes before tax is as follows:

    C = F£192 (pays F£75-00 tax, net income F£117-00)
    D = F£162 (pays F£50-00 tax, net income F£112-00)
    E = F£128 (pays F£24-58 tax, net income F£103-58)
    F = F£96 (pays F£6-00 tax, net income F£90-00)
    (average income = F£72)
    G = F£47 (receives F£13-41, net income F£60-41)
    H = F£32 (receives F£21-52, net income F£53-52)
    I = F£27 (receives F£24-15, net income F£51-15)
    J = F£17 (receives F£29-57, net income F£46-57)
    K = F£12 (receives F£32-20, net income F£44-20)
    L = F£7 (receives F£34-99, net income F£41-99)

    We can improve the efficiency of the program with some additional analysis. For example, the formula for the amount of tax paid means that for people earning more than A before tax the net earnings after tax are still more than A, so the only potential tax payers are C, D, E, and F.

  2. Brian Gladman 1 August 2016 at 11:11 pm
    from fractions import Fraction
    # convert pounds and pence into fractions of a pound
    def RF(x, y):
      return x + Fraction(y, 116)
    # the incomes after tax
    incomes = [ RF(117, 0), RF(112, 0), RF(103, 58), RF(90,  0), RF(60, 47),
                RF(53, 52), RF(51, 15), RF( 46, 57), RF(44, 20), RF(41, 99) ]
    # calculate the average income (which is the same before and after tax)
    A = sum(incomes) / 10
    # for tax payers income = earned - (earned - A)^2 / earned, giving
    # earned = A / (2 - income / A) -- note that when 'income' is more 
    # (less) than A, 'earned' is also more (less) than A
    earned, taxes = [], 0
    for i, income in enumerate(incomes):
      e = A / (2 - income / A)
      # tax is only paid on above average incomes 
      if e <= A:
      taxes += (e - income)
    # for N people receiving tax, the sum of their earned incomes is the
    # sum of their incomes less the total tax collected; the total of the
    # amounts by which their earned incomes are below the average income
    # is hence N.A - (sum(income) - taxes).
    deficit = (10 - i) * A - (sum(incomes[i:]) - taxes)
    # for non tax payers, income = earned + (A - earned).taxes / deficit
    # which gives earned = (income.deficit - A.taxes)  / (deficit - taxes)
    for income in incomes[i:]:
      e = (income * deficit - A * taxes)  / (deficit - taxes)
      earned += [e]
    # output the earned incomes for the ten people
    f, s, l = ', '.join('{}'.format(e) for e in earned).rpartition(', ')
    print('The pre-tax incomes of C..L (in F\xa3) are {} and {}.'.format(f, l))

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: