Enigmatic Code

Programming Enigma Puzzles

Enigma 1438: Soccer stats

From New Scientist #2599, 14th April 2007

There are five teams in our local league, and each team plays each of the others once a season. Below is the league table at the end of last season.

Enigma 1438

Unfortunately three of those numerical entries are incorrect. What were the scores in Arnsley’s four games?



One response to “Enigma 1438: Soccer stats

  1. Jim Randell 22 April 2013 at 2:23 pm

    A bit of analysis tells us in which part of the table the errors must lie, and this Python program does the rest. It’s a bit messy, but it runs in 71ms.

    # consider the sums of the columns:
    # won = 8, drawn = 7, lost = 5, for = 7, against = 7
    # each won game for one team is lost for another team, so
    # it should be that "won" = "lost", but they are different
    # hence there is at least one error in the "won" and "lost"
    # columns.
    # also each drawn game is drawn for two teams, so "drawn" should
    # be a multiple of 2, and it isn't so there is at least one
    # error in the "drawn" column.
    # each goal scored for a team is scored against another team
    # so these columns should be equal - and they are, so if there
    # was an error in one column there would have to be an error
    # in the other column to bring the values back into line, hence
    # there cannot be exactly one error in the "for" and "against"
    # columns (which is the maximum possibility), so these columns
    # must contain no errors.
    # hence all three errors are in the "won", "lost", "drawn" columns.
    from collections import namedtuple
    from itertools import product
    from enigma import irange, printf
    # the 10 matches are:
    # AvB AvC AvD AvE BvC BvD BvE CvD CvE DvE
    # each is either won, lost or drawn ('d')
    Table = namedtuple('Table', 'w l d')
    # generate the "won", "drawn", "lost" entries in the table
    def table(t, games):
      w, l, d = 0, 0, 0
      for g in games:
        if g == 'd':
          d += 1
        elif g == t:
          w += 1
          l += 1
      return Table(w, d, l)
    # generate possible scores for team <t> in games <games>
    # with total goals for <f> and total goals against <a>
    def scores(t, games, f, a):
      if len(games) == 0:
        if f == a == 0: yield []
        g = games[0]
        if g == 'd':
          for n in irange(0, min(f, a)):
            for s in scores(t, games[1:], f - n, a - n):
              yield [(n, n)] + s
        elif g == t:
          for n in irange(1, f):
            for m in irange(0, min(n - 1, a)):
              for s in scores(t, games[1:], f - n, a - m):
                yield [(n, m)] + s
          for n in irange(0, f):
            for m in irange(n + 1, a):
              for s in scores(t, games[1:], f - n, a - m):
                yield [(n, m)] + s
    # make the table for A
    for (AB, AC, AD, AE) in product('ABd', 'ACd', 'ADd', 'AEd'):
      A = table('A', (AB, AC, AD, AE))
      # count the discrepancies
      dA = sum(1 for (p, q) in zip(A, (2, 1, 1)) if p != q)
      # make the table for B
      for (BC, BD, BE) in product('BCd', 'BDd', 'BEd'):
        B = table('B', (AB, BC, BD, BE))
        # count the discrepancies
        dB = sum(1 for (p, q) in zip(B, (3, 1, 0)) if p != q)
        if dA + dB > 3: continue
        # table for C
        for (CD, CE) in product('CDd', 'CEd'):
          C = table('C', (AC, BC, CD, CE))
          dC = sum(1 for (p, q) in zip(C, (1, 3, 0)) if p != q)
          if dA + dB + dC > 3: continue
          # table for D and E
          for DE in 'DEd':
            D = table('D', (AD, BD, CD, DE))
            E = table('E', (AE, BE, CE, DE))
            dD = sum(1 for (p, q) in zip(D, (1, 2, 1)) if p != q)
            dE = sum(1 for (p, q) in zip(E, (1, 0, 3)) if p != q)
            d = dA + dB + dC + dD + dE
            if d != 3: continue
            # generate scores that match up to the "for" against "columns"
            for (sAB, sAC, sAD, sAE) in scores('A', [AB, AC, AD, AE], 2, 2):
              for (sBC, sBD, sBE) in scores('B', [BC, BD, BE], 2 - sAB[1], 1 - sAB[0]):
                for (sCD, sCE) in scores('C', [CD, CE], 1 - sAC[1] - sBC[1], 0 - sAC[0] - sBC[0]):
                  for (sDE,) in scores('D', [DE], 1 - sAD[1] - sBD[1] - sCD[1], 1 - sAD[0] - sBD[0] - sCD[0]):
                    if (sAE[1] + sBE[1] + sCE[1] + sDE[1], sAE[0] + sBE[0] + sCE[0] + sDE[0]) != (1, 3): continue
                    printf("AvB: {sAB}, AvC: {sAC}, AvD: {sAD}, AvE: {sAE}")
                    printf("BvC: {sBC}, BvD: {sBD}, BvE: {sBE}")
                    printf("CvD: {sCD}, CvE: {sCE}")
                    printf("DvE: {sDE}")

    Solution: The scores in Arnsley’s four games were – Arnsley vs. Boldham: 0 – 2; Arnsley vs. Cleeds: 0 – 0; Arnsley vs. Drochdale: 1 – 0; Arnsley vs. Erby: 1 – 0.

    The incorrect values are the “won”, “drawn” and “lost” values for Boldham.

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 )

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: