Updates from December, 2024 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 9:25 am on 6 December 2024 Permalink | Reply
    Tags:   

    Teaser 2618: Antics on the block 

    From The Sunday Times, 25th November 2012 [link] [link]

    A wooden cuboid-shaped block has a square base. All its sides are whole numbers of centimetres long, with the height being the shortest dimension. Crawling along three edges, an ant moves from a top corner of the block to the furthest-away bottom corner. In so doing it travels ten centimetres further than if it had chosen the shortest route over the surface of the block.

    What are the dimensions of the block?

    [teaser2618]

     
    • Jim Randell's avatar

      Jim Randell 9:25 am on 6 December 2024 Permalink | Reply

      If the square base has dimensions x by x, and the block has height h, where h < x.

      Opening up the cuboid like a cardboard box gives us the minimal distance = hypot(x, x + h).

      This gives a straightforward program:

      from enigma import (irange, ihypot, printf)
      
      # consider the size of the square
      for x in irange(2, 16):
        # consider the height
        for h in irange(1, x - 1):
          # shortest distance
          d = ihypot(x, x + h)
          if not d: continue
          if h + 2 * x == d + 10:
            printf("{x} x {x} x {h}")
      

      Solution: The block is: 15 cm × 15 cm × 5 cm.


      With a little analysis we can get a shorter program.

      We have:

      2x + h = hypot(x, x + h) + 10

      (2x + h − 10)² = x² + (x + h)²
      2x² + 2(h − 20)x + 20(5 − h) = 0
      x² + (h − 20)x + 10(5 − h) = 0

      So, by considering possible values of h, we get a quadratic in x, which can be solved for integer values.

      from enigma import (irange, quadratic, printf)
      
      # consider increasing heights
      for h in irange(1, 16):
        # find integer x values
        for x in quadratic(1, h - 20, 10 * (5 - h), domain='Z'):
          if x > h:
            printf("{x} x {x} x {h}")
      

      Like

  • Unknown's avatar

    Jim Randell 9:57 am on 3 December 2024 Permalink | Reply
    Tags:   

    Teaser 2600: Mileometer 

    From The Sunday Times, 22nd July 2012 [link] [link]

    My car’s digital mileometer has five digits, displaying whole miles. It also has a trip meter with five digits, displaying tenths of a mile (which resets to zero when reaching 10,000 miles). Soon after the car was new I reset the trip meter to zero (but never did that again). Not long after, I noticed that the two five-digit displays were the same ignoring the decimal point). The next time the displays were the same I wrote down the mileage and did so on each subsequent occasion until the mileage reached 100,000. The sum of all the mileages I wrote down was 500,000.

    What were the five digits when the displays were first the same?

    [teaser2600]

     
    • Jim Randell's avatar

      Jim Randell 9:57 am on 3 December 2024 Permalink | Reply

      Suppose the readings are the same at a distance ABCDE+F miles (the +F indicates + F tenths of a mile), then the readings are:

      milometer = ABCDE
      trip meter = ABCD+E

      The trip meter is actually reading XABCD+E miles, since it was reset, for some non-visible digit X.

      Which means the trip meter was reset at an actual distance of:

      ABCDE+F − XABCD+E

      And we want this to be soon after the car was new, maybe in the first 10K miles.

      This Python program looks at distances of possible duplicated readings, and collects them by the trip reset distance. It then looks for a distance where the sum of the milometer readings (except for the first one) is 500000.

      It runs in 530ms (using PyPy).

      from enigma import (defaultdict, irange, subsets, nconcat, trim, printf)
      
      # collect readings by reset distance
      rs = defaultdict(list)
      
      # consider possible 6-digit distances (in 0.1 mile units)
      for (A, B, C, D, E, F) in subsets(irange(0, 9), size=6, select='M'):
        dist = nconcat(A, B, C, D, E, F)
        for X in irange(max(0, A - 1), A):
          # consider trip distances that match the milometer (in 0.1 mile units)
          trip = nconcat(X, A, B, C, D, E)
          # calculate trip reset point
          k = dist - trip
          if not (0 < k < 100000): continue
          rs[k].append(dist)
      
      # output the milometer/trip readings
      def output(d, k):
        (m, f) = divmod(d, 10)
        (t, g) = divmod(d - k, 10)
        t %= 10000
        printf("@ {m:05d}.{f} miles -> {m:05d} + {t:04d}.{g}")
      
      # consider possible reset distances
      for (k, vs) in rs.items():
        vs = sorted(vs)
        # sum the milometer readings (except the first one)
        t = sum(v // 10 for v in trim(vs, head=1))
        if t == 500000:
          # output solution
          output(k, k)
          for v in vs: output(v, k)
          printf("t = {t}")
          printf()
      

      Solution: When the displays were first the same they were showing 01235 (or 0123.5 for the trip meter).

      The trip meter was reset as a distance of 1111.6 miles, and so the readings were:

      @ 01111.6 miles → 01111 + 0000.0 [trip reset]
      @ 01235.1 miles → 01235 + 0123.5 [1st equal display]
      @ 12346.2 miles → 12346 + 1234.6 [2nd equal display – noted down]
      @ 23457.3 miles → 23457 + 2345.7 [3rd equal display – noted down]
      @ 34568.4 miles → 34568 + 3456.8 [4th equal display – noted down]
      @ 45679.5 miles → 45679 + 4567.9 [5th equal display – noted down]
      @ 56790.6 miles → 56790 + 5679.0 [6th equal display – noted down]
      @ 67901.7 miles → 67901 + 6790.1 [7th equal display – noted down]
      @ 79012.8 miles → 79012 + 7901.2 [8th equal display – noted down]
      @ 90123.9 miles → 90123 + 9012.3 [9th equal display – noted down]
      @ 90124.0 miles → 90124 + 9012.4 [10th equal display – noted down]

      And the sum of the values noted down is:

      12346 + 23457 + 34568 + 45679 + 56790 + 67901 + 79012 + 90123 + 90124
      = 500000

      Like

  • Unknown's avatar

    Jim Randell 4:55 pm on 27 November 2024 Permalink | Reply
    Tags:   

    Teaser 2621: Come dancing 

    From The Sunday Times, 16th December 2012 [link] [link]

    Angie, Bianca, Cindy and Dot were given dance partners and performed in front of four judges, Juno, Kraig, Len and Marcia. Each judge placed the performances in order and gave 4 marks to the best, then 3, 2 and 1 point to the others. The dancers’ marks were then added up and they finished in alphabetical order with no ties. Angie’s winning margin over Bianca was larger than Bianca’s over Cindy, and Bianca’s winning margin over Cindy was larger than Cindy’s over Dot.

    Juno’s ordering of the four was different from the final order, and Kraig’s 4 points and Len’s 3 points went to dancers who were not in the top two.

    How many points did Cindy get from Juno, Kraig, Len and Marcia respectively?

    [teaser2621]

     
    • Jim Randell's avatar

      Jim Randell 4:56 pm on 27 November 2024 Permalink | Reply

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 90ms. (Internal runtime of the generated program is 10.5ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      #     A B C D
      #  J: a b c d
      #  K: e f g h
      #  L: i j k m
      #  M: n p q r
      
      --digits="1-4"
      --distinct="abcd,efgh,ijkm,npqr"
      
      # totals for each
      --macro="@A = ({a} + {e} + {i} + {n})"
      --macro="@B = ({b} + {f} + {j} + {p})"
      --macro="@C = ({c} + {g} + {k} + {q})"
      --macro="@D = ({d} + {h} + {m} + {r})"
      
      # A's margin over B is larger than B's margin over C
      "@A - @B > @B - @C" "@B - @C > 0"
      # B's margin over C is larger than C's margin over D
      "@B - @C > @C - @D" "@C - @D > 0"
      
      # J's order was different from the final order, so not (4, 3, 2, 1)
      "({a}, {b}, {c}, {d}) != (4, 3, 2, 1)"
      
      # K's 4 did not go to A or B
      --invalid="4,ef"
      
      # L's 3 did not go to A or B
      --invalid="3,ij"
      
      --template="A=({a} {e} {i} {n}) B=({b} {f} {j} {p}) C=({c} {g} {k} {q}) D=({d} {h} {m} {r})"
      --solution=""
      --verbose="1-H"
      

      Solution: Cindy’s points were: Juno = 1, Kraig = 4, Len = 1, Marcia = 2.

      The complete scores are (J + K + L + M):

      A: 4 + 3 + 4 + 4 = 15
      B: 3 + 2 + 2 + 3 = 10
      C: 1 + 4 + 1 + 2 = 8
      D: 2 + 1 + 3 + 1 = 7

      Like

    • Ruud's avatar

      Ruud 8:25 am on 28 November 2024 Permalink | Reply

      import itertools
      
      
      def order(lst):
          return [lst.index(el) for el in sorted(lst, reverse=True)]
      
      
      for juno, kraig, len, marcia in itertools.product(itertools.permutations(range(1, 5)), repeat=4):
          totals = [sum(x) for x in zip(juno, kraig, len, marcia)]
          if sorted(totals, reverse=True) == totals:
              if totals[0] - totals[1] > totals[1] - totals[2] > totals[2] - totals[3] > 0:
                  if order(juno) != order(totals):
                      if kraig.index(4) in (2, 3) and len.index(3) in (2, 3):
                          print(juno[2], kraig[2], len[2], marcia[2])
      

      Like

  • Unknown's avatar

    Jim Randell 8:40 am on 22 November 2024 Permalink | Reply
    Tags:   

    Teaser 2605: Simple sums 

    From The Sunday Times, 26th August 2012 [link] [link]

    I have taken some numbers and consistently replaced the digits by letters, with different letters for different digits.

    In this way:

    TWO + TWO = FOUR;
    FIVE is prime;
    FIVETWO is prime;
    THREE is divisible by 3.

    Quelle est la valeur de HUIT?

    [teaser2605]

     
    • Jim Randell's avatar

      Jim Randell 8:41 am on 22 November 2024 Permalink | Reply

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 79ms. (Internal runtime of the generated program is 330µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "TWO + TWO = FOUR"
      "is_prime(FIVE)"
      "is_prime(FIVE - TWO)"
      "THREE % 3 = 0"
      
      --answer="HUIT"
      

      Solution: HUIT = 4539.

      We have:

      TWO = 928
      THREE = 94677
      FOUR = 1856
      FIVE = 1307

      Like

      • Frits's avatar

        Frits 4:28 am on 23 November 2024 Permalink | Reply

        @Jim, your first run members had option -r on line 1 but the newer ones have option -rr.
        Can you explain the difference as I have problems finding it in enigma.py.

        Like

        • Jim Randell's avatar

          Jim Randell 10:09 am on 23 November 2024 Permalink | Reply

          The -r option is short for the --run option, which also allows you to specify the type of the file to run.

          The following additional arguments are accepted by the --run option:

          --run:type=.run (or -rr)
          --run:type=.py (or -rp)
          --run:timed (or -rt)
          --run:verbose (or -rv)

          If the type is not specified then it is set from the file extension. But specifying the type as an argument allows the code to run correctly, even if it is saved to a file that does not have a .run extension.

          Like

    • Ruud's avatar

      Ruud 8:06 am on 23 November 2024 Permalink | Reply

      Very brute force

      import istr
      
      for t, w, o, f, u, r, e, h, v, i in istr.permutations(range(10), 10):
          if (
              (t | w | o) + (t | w | o) == (f | o | u | r)
              and (f | i | v | e).is_prime()
              and ((f | i | v | e) - (t | w | o)).is_prime()
              and (t | h | r | e | e).is_divisible_by(3)
              and t * f * h
          ):
              print(f"two={t|w|o} four={f|o|u|r} five={f|i|v|e} three={t|h|r|e|e} huit={h|u|i|t}")
      

      Like

      • Frits's avatar

        Frits 12:49 pm on 23 November 2024 Permalink | Reply

        @Ruud, I thought you had forgotten about the non-zero first characters but it is there at line 9.

        The istr concatenation character might become aesthetically problematic when there is a letter L. In that case would you use upper case variables?

        Like

        • Ruud's avatar

          Ruud 4:21 pm on 23 November 2024 Permalink | Reply

          In contrast to most contributors here, I try and follow PEP8 conventions, which implies lowercase variables.

          Like

  • Unknown's avatar

    Jim Randell 11:16 am on 19 November 2024 Permalink | Reply
    Tags:   

    Teaser 2613: Anagrammatical 

    From The Sunday Times, 21st October 2012 [link] [link]

    The number 258 is special in the following way. If you write down its six different anagrams (258, 285, 528, etc.), then calculate the fifteen differences between any two of those six (27, 270, 243, etc.) and then add up those fifteen differences, you get 4644, which is a multiple of the number 258 that you started with!

    Which higher three-figure number has the same property?

    [teaser2613]

     
    • Jim Randell's avatar

      Jim Randell 11:17 am on 19 November 2024 Permalink | Reply

      Here is a constructive solution.

      It runs in 57ms. (Internal runtime is 3.1ms).

      from enigma import (irange, nsplit, subsets, nconcat, printf)
      
      # check a 3 digit number
      def check(n):
        # find different rearrangements of the digits
        rs = sorted(subsets(nsplit(n), size=len, select='mP', fn=nconcat))
        if len(rs) < 2: return False
        # calculate the total sum of the pairwise differences
        t = sum(y - x for (x, y) in subsets(rs, size=2))
        if t % n != 0: return False
        printf("{n} -> {t}")
        return True
      
      # check the given value
      assert check(258)
      
      # find the next 3-digit number after 258 that works
      for n in irange(259, 999):
        if check(n): break
      

      Solution: The next 3-digit number with the same property is 387.

      And there are no higher 3-digit numbers with the property, but there are lower ones.

      The complete list of 3-digit numbers (and the sum of the differences) with this property is:

      129 → 6192
      172 → 4644
      258 → 4644
      387 → 3870

      Like

      • Frits's avatar

        Frits 5:03 pm on 21 November 2024 Permalink | Reply

        The differences between any two of these four 3-digit numbers are multiples of 43.

        See [https://brg.me.uk/?page_id=300].

        Like

    • Ruud's avatar

      Ruud 7:18 pm on 19 November 2024 Permalink | Reply

      This code finds all three digit numbers that meet the conditions given:

      import istr
      
      for i in istr.range(100, 1000):
          if i.all_distinct() and sum(abs(x - y) for x, y in istr.combinations(istr.concat(istr.permutations(i)), 2)).is_divisible_by(i):
              print(i)
      

      Like

    • GeoffR's avatar

      GeoffR 2:56 pm on 21 November 2024 Permalink | Reply

      from enigma import nsplit
      
      for n in range(258, 999):
          diff = 0
          a, b, c = nsplit(n)
          if 0 in (a, b, c):
              continue
          # Form the six numbers
          n1, n2 = 100*a + 10*b + c, 100*a + 10*c + b
          n3, n4 = 100*b + 10*a + c, 100*b + 10*c + a
          n5, n6 = 100*c + 10*a + b, 100*c + 10*b + a
          if not len(set((n1, n2, n3, n4, n5, n6))) == 6:
              continue
          # Find sum of fifteen differences
          diff = abs(n1-n2) + abs(n1-n3) + abs(n1-n4) + abs(n1-n5) + abs(n1-n6)\
                  + abs(n2-n3) + abs(n2-n4) + abs(n2-n5) + abs(n2-n6)\
                  + abs(n3-n4) + abs(n3-n5) + abs(n3-n6)\
                  + abs(n4-n5) + abs(n4-n6)\
                  + abs(n5-n6)
          #  Sum of fifteen differences is a multiple of the number 
          if diff % n == 0:
              print(f"Number = {n}, Differences sum = {diff}")
      
      # Number = 258, Differences sum = 4644
      # Number = 387, Differences sum = 3870
      
      

      Interesting that the differences sum is ten times the number.

      Like

  • Unknown's avatar

    Jim Randell 8:43 am on 14 November 2024 Permalink | Reply
    Tags: ,   

    Teaser 2545: Emblematic 

    From The Sunday Times, 3rd July 2011 [link] [link]

    Pat’s latest art installation consists of a large triangle with a red border and, within it, a triangle with a green border. To construct the green triangle, he drew three lines from the vertices of the red triangle to points one third of the way (clockwise) along their respective opposite red sides. Parts of these lines formed the sides of the green triangle. In square centimetres, the area of the red triangle is a three-digit number and the area of the green triangle is the product of those three digits.

    What is the area of the red triangle?

    [teaser2545]

     
    • Jim Randell's avatar

      Jim Randell 8:46 am on 14 November 2024 Permalink | Reply

      See also: Teaser 3233, Teaser 2865Enigma 1076Enigma 320Enigma 1313.

      This is another puzzle that can be solved using Routh’s Theorem [@wikipedia], which I have made notes on here [ rouths-theorem.pdf ].

      This Python program runs in 70ms. (Internal runtime is 3.5ms).

      from enigma import (irange, inf, rdiv, multiply, nsplit, printf)
      
      # ratio XYZ/ABC in Routh's theorem
      def routh(x, y, z):
        a = x * y * z - 1
        b = (x * y + y + 1) * (y * z + z + 1) * (z * x + x + 1)
        return rdiv(a * a, b)
      
      # compute the ratio r = green / red
      r = routh(2, 2, 2)
      
      # consider possible areas for the smaller (green) triangle
      for G in irange(1, inf):
        # calculate the area of the larger (red) triangle
        R = rdiv(G, r)
        # R is a 3-digit number
        if R > 999: break
        if R < 100 or R % 1 > 0: continue
        # the product of R's digits is G
        if not (multiply(nsplit(R)) == G): continue
      
        # output solution
        printf("G={G} R={R} [r={r}]")
      

      Solution: The area of the red triangle is: 735 cm².

      And the area of the green triangle is: 105 cm².

      7 × 3 × 5 = 105


      From Routh’s Theorem we determine that area of the green triangle is 1/7 that of the red triangle.

      So we are looking for a 3-digit number ABC such that:

      A × B × C = ABC / 7

      The following run file executes in 73ms. (Internal runtime of the generated program is 144µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "7 * A * B * C = ABC"
      
      --answer="ABC"
      

      Like

  • Unknown's avatar

    Jim Randell 9:03 am on 12 November 2024 Permalink | Reply
    Tags:   

    Teaser 2543: Hit and miss 

    From The Sunday Times, 19th June 2011 [link] [link]

    For calculations using arithmetic in a base higher than 10 I need symbols for the higher “digits”. My digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A (= 10), B (= 11), C (= 12), etc., using as many letters as necessary. But some numbers are ambiguous, e.g. in HIT and in MISS it is unclear whether the second digit is the number 1 or the letter I. So there are four interpretations of HIT + MISS. I’ve taken one of those four interpretations and worked out its remainder when dividing by four. If you knew the remainder you could work out which base I am using.

    What is that base?

    [teaser2543]

     
    • Jim Randell's avatar

      Jim Randell 9:03 am on 12 November 2024 Permalink | Reply

      There is a T (= 29), so the the minimum possible base is 30, and I am assuming that the maximum base is 36 (as Z = 35). (And if we consider bases larger than 36 then the puzzle has no solutions).

      The following Python program runs in 67ms. (Internal runtime is 199µs).

      from enigma import (defaultdict, irange, cproduct, base2int, join, printf)
      
      # consider possible interpretations of the sum
      for terms in cproduct([["HIT", "H1T"], ["MISS", "M1SS"]]):
      
        # consider possible bases, and collect candidate bases by residue mod 4
        d = defaultdict(list)
        for b in irange(30, 36):
          ns = list(base2int(t, base=b) for t in terms)
          r = sum(ns) % 4
          d[r].append(b)
      
        # look for residues that give a unique base
        for (r, bs) in d.items():
          if len(bs) == 1:
            printf("r={r} -> base={b} [sum={terms}]", b=bs[0], terms=join(terms, sep='+'))
      

      Solution: The base is 33.

      The values to be summed are: H1T (= 18575) and M1SS (= 792655), i.e. both with a digit 1 (one), rather than I (capital i).

      The total is 811230, which has a residue of 2 (mod 4), and 33 is the only base that gives a residue of 2.


      Manually:

      We note that increasing the base by 4 will not change the residue modulo 4 of the sum.

      So the following pairs of bases will always give the same mod 4 residue: (30, 34), (31, 35), (32, 36).

      This leaves 33 as the only base for which it is possible for the residue to be unique, and this solves the puzzle. (And it also explains why the puzzle has no solutions if base 37 is included).

      Like

    • ruudvanderham's avatar

      ruudvanderham 12:31 pm on 12 November 2024 Permalink | Reply

      Like Jim’s solution:

      import istr
      
      remainders = {i: [] for i in range(4)}
      for base in range(30, 37):
          istr.base(base)
          for s in istr("HIT MISS", "H1T MISS", "HIT M1SS", "H1T M1SS"):
              s1, s2 = s.split()
              remainders[int(s1 + s2) % 4].append((base, s1, s2))
      for i in range(4):
          if len(remainders[i]) == 1:
              print(remainders[i])
      

      Like

    • GeoffR's avatar

      GeoffR 8:56 pm on 12 November 2024 Permalink | Reply

      # Given letter values
      H, I, M, S, T = 17, 18, 22, 28, 29
      
      i = 1
      
      from collections import defaultdict
      rem = defaultdict(list)
      
      # Four possible values of (HIS + MISS)
      for base in range(30, 37):
          # letter I + letter I
          MISS = M*base**3 + I*base**2 + S*base + S
          HIT = H*base*base + I*base + T
          r = (HIT + MISS) % 4
          rem [r] += [base, HIT, MISS]
          
          # Letter I + digit 1
          MISS = M*base**3 + I*base**2 + S*base + S
          HiT = H*base*base + i*base + T
          r = (HiT + MISS) % 4
          rem [r] += [base, HiT, MISS]
          
          # digit 1 + letter I
          MiSS = M*base**3 + i*base**2 + S*base + S
          HIT = H*base*base + I*base + T
          r = (HIT + MiSS) % 4
          rem [r] += [base, HIT, MiSS]
          
          # digit  1 + digit 1
          M1SS = M*base**3 + 1*base**2 + S*base + S
          H1T = H*base*base + 1*base + T
          r = (H1T + M1SS) % 4
          rem [r] += [base, H1T, M1SS]
      
      for k,v in rem.items():
          if len(v) == 3:
              print(k, v)
              print("base = ", v[0])
      
      
      # 2 [33, 792655, 18575]
      # base =  33
      
      
      

      Like

    • Jim Randell's avatar

      Jim Randell 5:44 pm on 13 November 2024 Permalink | Reply

      @ruud & @geoff:

      I don’t think it is correct to collect all the values for the different possible sums together in the same dictionary.

      The setter is using one of the possible sums and the residue uniquely identifies the base for that sum. But there is nothing to say that the residue is unique across all the sums (although in this case it is). But if one of the other sums had the same residue it would mean your code would not identify the solution.

      For example, if we used bases 31 – 37 in the puzzle (instead of 30 – 36) the solution would be that the base was 34. (The terms are still H1T and M1SS, but the residue is 3).

      Like

  • Unknown's avatar

    Jim Randell 10:47 am on 8 November 2024 Permalink | Reply
    Tags: ,   

    Teaser 2616: Elevenses 

    From The Sunday Times, 11th November 2012 [link] [link]

    On 11/11 it is perhaps appropriate to recall the following story. In the graphics class students were illustrating pairs of similar triangles. In Pat’s larger triangle the sides were all even whole numbers divisible by 11. In fact they were 22 times the corresponding sides of his smaller triangle. As well as this, in the smaller triangle the digits used overall in the lengths of the three sides were all different and did not include a zero. Miraculously, exactly the same was true of the larger triangle.

    What were the sides of Pat’s smaller triangle?

    [teaser2616]

     
    • Jim Randell's avatar

      Jim Randell 10:47 am on 8 November 2024 Permalink | Reply

      We can place some bounds on the sides of the triangles:

      The smaller triangle cannot have sides less than 6, as the corresponding side in the larger triangle would have repeated digits.

      And so the smallest side in the larger triangle must have at least 3 digits, and as there are only 9 digits available, each side of the larger triangle must have 3 digits, and so the sides of the smaller triangle are between 6 and 43.

      This Python program collects possible sides for the large and small triangles, and then chooses three pairs that can form viable triangles.

      It runs in 66ms. (Internal runtime is 1.04ms).

      from enigma import (irange, is_duplicate, subsets, join, printf)
      
      # check for allowable digits
      def check(*vs):
        s = join(vs)
        if '0' in s or is_duplicate(s): return False
        return True
      
      # find multiples of 22 with distinct non-zero digits
      d = dict()  # d maps n -> n / 22
      for i in irange(6, 43):
        if not check(i): continue
        j = i * 22
        if not check(j): continue
        d[j] = i
      
      # choose the two smaller sides
      for (a, b) in subsets(d.keys(), size=2):
        if not (check(a, b) and check(d[a], d[b])): continue
      
        # choose the longer side
        for c in d.keys():
          # check for viable triangle
          if not (b < c): continue
          if not (c < a + b): break
          # check the digits
          if not (check(a, b, c) and check(d[a], d[b], d[c])): continue
          # output solution
          printf("{t1} -> {t2}", t2=(a, b, c), t1=(d[a], d[b], d[c]))
      

      Solution: The sides of the smaller triangle are: 18, 26, 37.

      And the corresponding sides of the larger triangle are: 396, 572, 814.

      Like

    • Frits's avatar

      Frits 1:46 pm on 8 November 2024 Permalink | Reply

      from enigma import SubstitutedExpression
       
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          "div(ABC, 22) = PQ", 
          "A < D",
          "div(DEF, 22) = RS",
          "D < G",
          "div(GHI, 22) = TU",
       
          # allow for single digit sides of smaller triangle   
          "P == 0 or P not in {Q, R, S, T, U}",
          "R == 0 or R not in {P, Q, S, T, U}",
          "T == 0 or T not in {P, Q, R, S, U}",
          
          # viable triangle
          "TU < PQ + RS",
          
          "0 not in {A, B, C, D, E, F, G, H, I, Q, S, U}",
        ],
        answer="PQ, RS, TU",
        d2i="",
        distinct=("ABCDEFGHI","QSU"),
        verbose=0,    # use 256 to see the generated code
      ) 
      
      # print answers
      for ans in p.answers():
        print(f"answer: {ans}")
      

      Like

    • GeoffR's avatar

      GeoffR 11:16 am on 9 November 2024 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Smaller triangle - assumed 2 digit sides
      var 1..9:a; var 1..9:b;  var 1..9:c; var 1..9:d; var 1..9:e; var 1..9:f;
      
      constraint all_different([a, b, c, d, e, f]);
      var 12..98:ab == 10*a + b;
      var 12..98:cd == 10*c + d;
      var 12..98:ef == 10*e + f;
      constraint ab < cd /\ cd < ef;  % order sides
      
      % Larger triangle - assumued 3 digit sides
      var 1..9:A; var 1..9:B;  var 1..9:C; 
      var 1..9:D; var 1..9:E;  var 1..9:F;
      var 1..9:G; var 1..9:H;  var 1..9:I;
      
      constraint all_different([A, B, C, D, E, F, G, H, I]);  
      var 123..987:ABC == 100*A + 10*B + C;
      var 123..987:DEF == 100*D + 10*E + F;
      var 123..987:GHI == 100*G + 10*H + I;
      constraint ABC < DEF /\ DEF < GHI;  % order sides
      
      % Larger triangle has even sides and all sides are divisible by 11
      constraint sum([ABC mod 2 == 0, DEF mod 2 == 0, GHI mod 2 == 0]) == 3;
      constraint sum([ABC mod 11 == 0, DEF mod 11 == 0, GHI mod 11 == 0]) == 3;
      
      % larger triangle's sides are 22 times smaller triangle's sides
      constraint ABC == 22 * ab /\ DEF == 22 * cd /\ GHI == 22 * ef;
      
      solve satisfy;
      
      output ["Smaller Triangle Sides = " ++ show(ab) ++ ", " ++ show(cd) ++ ", " ++ show(ef)
      ++ "\n" ++ "Larger Triangle Sides = " ++ show(ABC) ++ ", " ++ show(DEF) ++ ", " ++ show(GHI)
      ];
      
      % Smaller Triangle Sides = 18, 26, 37
      % Larger Triangle Sides = 396, 572, 814
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:58 am on 5 November 2024 Permalink | Reply
    Tags:   

    Teaser 2608: Football logic 

    From The Sunday Times, 16th September 2012 [link] [link]

    In the logicians’ football tournament, each of three teams (captained by Alf, Bert and Charlie) plays each other once, with three points for a win and one for a draw. In working out their order at the end of the tournament, “goals scored” are used to determine the order of teams with the same points total. Each captain only knows the scores in his own team’s games. At the end I asked the captains in turn whether they knew which position they had finished in. The replies were:

    Alf: “no”;
    Bert: “no”;
    Charlie: “no”;
    Alf: “yes”.

    In which position did Charlie’s team finish? And what was the result in the game between the other two teams?

    [teaser2608]

     
    • Jim Randell's avatar

      Jim Randell 8:01 am on 5 November 2024 Permalink | Reply

      If a captain knows that they are tied on points with another team, then they cannot know which way the tie breaking will go, as they do not know how many goals the other team scored in their remaining match, so any situation where teams are tied on points will necessarily require the captain to respond that they do not know their final position.

      This Python program uses the [[ Football() ]] helper class, and the [[ filter_unique() ]] function from the enigma.py library to solve the puzzle.

      It runs in 69ms. (Internal runtime is 838µs).

      from enigma import (Football, filter_unique, printf)
      
      # scoring system
      football = Football(games='wdl', points=dict(w=3, d=1))
      
      # calculate positions from points, ties are indicated with '='
      def pos(pts):
        rs = [None] * len(pts)
        p = 1
        for x in sorted(set(pts), reverse=1):
          k = pts.count(x)
          v = str(p)
          if k > 1: v += '='
          for (i, y) in enumerate(pts):
            if y == x:
              rs[i] = v
          p += k
        return tuple(rs)
      
      is_tie = lambda x: x.endswith('=')
      
      # generate possible situations
      def generate():
        # consider possible game outcomes
        for (AB, AC, BC) in football.games(repeat=3):
          # table for each team
          A = football.table([AB, AC], [0, 0])
          B = football.table([AB, BC], [1, 0])
          C = football.table([AC, BC], [1, 1])
          # return (<match-outcomes>, <positions>)
          yield ((AB, AC, BC), pos([A.points, B.points, C.points]))
      
      # indices for the teams, and the matches
      (A, B, C) = (AB, AC, BC) = (0, 1, 2)
      matches = { A: (AB, AC), B: (AB, BC), C: (AC, BC) }
      
      # can team <j> deduce their finishing position knowing the outcomes of their own matches?
      # <f> = 0 if the answer is "no"; = 1 if it is "yes"
      def answer(ss, j, f):
        # which matches is team <j> involved in?
        ms = matches[j]
        fnf = lambda t: tuple(t[0][i] for i in ms)
        fng = lambda t: t[1][j]
        # find results with unique/non-unique positions
        r = filter_unique(ss, fnf, fng)
        if f == 0:
          # return results that are non-unique, or end with a tie on points
          return r.non_unique + [t for t in r.unique if is_tie(fng(t))]
        else:
          # return results that are unique, but don't end with a tie on points
          return [t for t in r.unique if not is_tie(fng(t))]
      
      # start with all possible outcomes and positions
      ss = list(generate())
      
      # A cannot deduce his finishing position
      ss = answer(ss, A, 0)
      
      # B cannot deduce his finishing position
      ss = answer(ss, B, 0)
      
      # C cannot deduce his finishing position
      ss = answer(ss, C, 0)
      
      # A can now deduce his finishing position
      ss = answer(ss, A, 1)
      
      # output solutions
      for ((AB, AC, BC), (posA, posB, posC)) in ss:
        printf("posC = {posC}; AB = {AB} [AB={AB} AC={AC} BC={BC}; posA={posA} posB={posB} posC={posC}]")
      

      Solution: Charile finished in second place. The game between Alf and Bert was a draw.

      The match outcomes are either:

      (A v B = draw; C beat A; B beat C)
      B = 1w 1d = 4 pts (1st)
      C = 1w = 3 pts (2nd)
      A = 1d = 1 pt (3rd)

      or:

      (A v B = draw; A beat C; C beat B)
      A = 1w 1d = 4 pts (1st)
      C = 1w = 3pts (2nd)
      B = 1d = 1pt (3rd)

      Like

  • Unknown's avatar

    Jim Randell 7:44 am on 31 October 2024 Permalink | Reply
    Tags: ,   

    Teaser 2597: Ages ago 

    From The Sunday Times, 1st July 2012 [link] [link]

    Bert’s age in years is one less than one-and-a-half times the age Alf was a whole number of years ago. Cal’s age in years is one less than one-and-a-half times the age Bert was, the same number of years ago.

    Dave’s age in years is one less than one-and-a-half times the age Cal was, again the same number of years ago. All four ages are different two-figure numbers, Cal’s age being Bert’s age with the order of the digits reversed.

    What (in alphabetical order) are their ages?

    [teaser2597]

     
    • Jim Randell's avatar

      Jim Randell 7:45 am on 31 October 2024 Permalink | Reply

      Here’s a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run file executes in 75ms. (Internal runtime of the generated program is 956µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # ages now:
      #
      #   AB = Alf
      #   CD = Bert
      #   DC = Cal
      #   EF = Dave
      #
      # let the number of years ago be XY
      
      --distinct="CD"
      --invalid="0,ACDE"
      
      # "B's age is 1 less than 1.5 times A's age XY years ago"
      "3 * (AB - XY) == 2 * (CD + 1)"
      
      # "C's age is 1 less than 1.5 times B's age XY years ago"
      "3 * (CD - XY) == 2 * (DC + 1)"
      
      # "D's age is 1 less than 1.5 times C's age XY years ago"
      "3 * (DC - XY) == 2 * (EF + 1)"
      
      # ages are all different
      "all_different(AB, CD, DC, EF)"
      
      --template="(AB CD DC EF) (XY)"
      --solution=""
      

      Solution: The ages now are: Alf = 98; Bert = 86; Cal = 68; Dave = 41.

      And Cal’s age is the reverse of Bert’s.

      40 years ago, Alf was 58. And 1.5× this age is 87, and Bert’s current age is one less than this.

      40 years ago, Bert was 46. And 1.5× this age is 69, and Cal’s current age is one less than this.

      40 years ago, Cal was 28. And 1.5× this age is 42, and Dave’s current age is one less than this.

      Like

    • Ruud's avatar

      Ruud 8:38 am on 31 October 2024 Permalink | Reply

      Brute force:

      import itertools
      
      for a, b, d in itertools.permutations(range(10, 100), 3):
          c = int(str(b)[::-1])
          if b != c and c >= 10:
              for n in range(1, min(a, b, c, d) + 1):
                  if b == (a - n) * 1.5 - 1:
                      if c == (b - n) * 1.5 - 1:
                          if d == (c - n) * 1.5 - 1:
                              print(f"Alf={a} Bert={b} Cal={c} Dave={d} Number of years ago={n}")
      

      Liked by 1 person

    • ruudvanderham's avatar

      ruudvanderham 9:26 am on 31 October 2024 Permalink | Reply

      Quite different and much more efficient than my previous one:

      for a in range(10, 100):
          for n in range(a):
              b = (a - n) * 1.5 - 1
              c = (b - n) * 1.5 - 1
              d = (c - n) * 1.5 - 1
              if all(x == round(x) and n < x < 100 for x in (b, c, d)):
                  if len({a, b, c, d}) == 4:
                      if str(round(b)) == str(round(c))[::-1]:
                          print(a, b, c, d, n)
      

      Like

    • GeoffR's avatar

      GeoffR 9:35 am on 1 November 2024 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % All ages are different 2-digit numbers
      var 10..99:Alf; var 10..99:Bert; var 10..99:Cal; var 10..99:Dave;
      constraint all_different([Alf, Bert, Cal, Dave]);
      
      var 1..99:Y;  % the number of years ago
      
      constraint (2 * Bert ==  (Alf - Y) * 3 - 2)
      /\ (2 * Cal  ==  (Bert - Y) * 3 - 2)
      /\ (2 * Dave ==  (Cal - Y) * 3 - 2);
      
      % Cal’s age being Bert’s age with the order of the digits reversed.
      constraint Cal div 10 == Bert mod 10 /\ Cal mod 10 == Bert div 10;
      
      solve satisfy;
      
      output ["Alf = " ++ show(Alf) ++ ", Bert = " ++ show(Bert) ++
      ", Cal = " ++ show(Cal) ++ ", Dave = " ++ show(Dave) ++ ", Y = " ++ show(Y)];
      
      % Alf = 98, Bert = 86, Cal = 68, Dave = 41, Y = 40 
      % ----------
      % ==========
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:10 am on 29 October 2024 Permalink | Reply
    Tags:   

    Teaser 2540: Extra time 

    From The Sunday Times, 29th May 2011 [link] [link]

    George and Martha planned a holiday on the south coast. The second-class rail fare each way was a certain whole number of pounds per person and the nightly cost of an inexpensive double room, in pounds, was the same number but with digits reversed. They originally planned to stay 30 nights, but then increased that to 33. “So the total cost will go up by 10%”, said Martha. “No”, replied George, “it will go up by some other whole number percentage”.

    What is the nightly cost of a double room?

    [teaser2540]

     
    • Jim Randell's avatar

      Jim Randell 8:11 am on 29 October 2024 Permalink | Reply

      I assumed that the rail fare does not have a trailing zero, so that the room rate does not have a leading zero.

      This Python program finds the first solution where the rail fare and room rate are less than £100.

      It runs in 80ms. (Internal runtime is 188µs).

      from enigma import (irange, nrev, div, printf)
      
      # consider possible rail fares
      for F in irange(1, 99):
        # disallow trailing zeros
        if F % 10 == 0: continue
      
        # the room rate is the reverse of this
        R = nrev(F)
      
        # cost for 30 nights and 33 nights
        t30 = 4*F + 30*R
        t33 = 4*F + 33*R
      
        # t33 is a k per cent increase on t30
        r = div(100 * t33, t30)
        if r is None: continue
      
        printf("F={F} R={R} r={r}%")
        break
      

      Solution: The cost of the room was £ 54 per night.

      And so the rail fares were £ 45 per person (single).

      The cost for 30 days is:

      4× 45 + 30× 54 = 1800

      And the cost for 33 days:

      4× 45 + 33× 54 = 1962

      And we have:

      1962 / 1800 = 1.09

      So the increase is 9%.


      There are larger solutions, for example:

      F=45, R=54
      F=495, R=594
      F=4545, R=5454
      F=4995, R=5994
      F=45045, R=54054
      F=49995, R=59994
      F=450045, R=540054
      F=454545, R=545454
      F=495495, R=594594
      F=499995, R=599994

      But the room is described as “inexpensive”, so only the first of these is a viable solution.

      However, they all involve a 9% increase.

      Like

    • GeoffR's avatar

      GeoffR 12:34 pm on 29 October 2024 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; 
      var 1..99: per_cent;
      
      % Assume room cost < £100
      var 12..98: Room == 10*A + B; 
      var 12..98: Fare == 10*B + A;
      
      % Two people with outgoing and return trips = 4 fares
      var 600..6000: Cost30 == 30 * Room + 4 * Fare;
      var 660..6060: Cost33 == 33 * Room + 4 * Fare;
      
      % Calculate percentage increased room cost
      constraint (Cost33 - Cost30) * 100 ==  Cost30 * per_cent;
      
      solve satisfy;
      
      output ["Room cost = " ++ show(Room) ++ " pounds per night."
      ++ "\n" ++ "Cost percentage increase = " ++ show(per_cent) ++ " %"];
      
      % Room cost = 54 pounds per night.
      % Cost percentage increase = 9 %
      % ----------
      % ==========
      

      Like

    • Ruud's avatar

      Ruud 4:15 am on 30 October 2024 Permalink | Reply

      from istr import istr
      
      
      istr.repr_mode("int")
      for train in istr(range(10, 100)):
          room = train[::-1]
          if room[0] != 0:
              total30 = int(train * 4 + room * 30)
              total33 = int(train * 4 + room * 33)
              increase = ((total33 - total30) / total30) * 100
              if increase % 1 == 0:
                  print(f"{train=} {room=} {total30=} {total33=} {increase=:.0f}%")
      

      Like

    • Frits's avatar

      Frits 5:43 am on 3 November 2024 Permalink | Reply

      Objective was to use fewer iterations.

      from fractions import Fraction as RF
      
      # fare = 10 * f + g, room cost = 10 * g + f
      
      # t30 = 4*F + 30*R = 70*f + 304*g  
      # t33 = 4*F + 33*R = 73*f + 334*g
      
      # 100 + p = 100 * (73*f + 334*g) / (70*f + 304*g)     
      
      # integer percentages less than 10
      for p in range(9, 0, -1):
        # (7300*f + 33400*g) - (100 + p) * (70*f + 304*g) = 0
        # ((300 - p*70)*f + (3000 - 304*p)*g) = 0
        
        # calculate - f / g  (g will not be zero)
        r = RF(3000 - 304*p, 300 - p*70)
        if r > 0: continue  
        f, g = abs(r.numerator), abs(r.denominator)
        if not (0 < f < 10 and g < 10): continue 
        
        # double check
        if 100 * (73*f + 334*g) / (70*f + 304*g) - 100 != p: continue
        print(f"answer: {10 * g + f} pounds per night") 
      

      Like

  • Unknown's avatar

    Jim Randell 8:04 am on 22 October 2024 Permalink | Reply
    Tags:   

    Teaser 2539: Doubling up 

    From The Sunday Times, 22nd May 2011 [link] [link]

    One day at school I found myself in charge of two classes. Faced with 64 pupils and only 32 desks, I gave each child a different whole number and told them the highest number must share with the lowest, the second highest with the second lowest, etc. When they had sorted themselves into pairs, I got each child to multiply their own number by that of their desk-mate. They discovered all the products were the same. In fact I chose the original numbers so that this common product would be as low as possible.

    What was the common product?

    [teaser2539]

     
    • Jim Randell's avatar

      Jim Randell 8:04 am on 22 October 2024 Permalink | Reply

      I am assuming that the “whole numbers” were are interested in are positive integers.

      We are looking for the smallest number with at least 64 divisors (so that we can form 32 different pairs).

      Fortunately the answer is fairly small, so we can use a simple search.

      The following Python program runs in 89ms. (Internal runtime is 17ms).

      from enigma import (irange, inf, tau, arg, printf)
      
      N = arg(64, 0, int)
      
      # find the smallest number with _at least_ N divisors
      for n in irange(1, inf):
        if tau(n) >= N:
          printf("{N} divisors -> n = {n}")
          break
      

      Solution: The product was 7560.

      It turns out that 7560 (= (2^3)(3^3)(5)(7)) has exactly 64 divisors, and so here are the 32 pairs:

      >>> list(divisors_pairs(7560))
      [(1, 7560), (2, 3780), (3, 2520), (4, 1890), (5, 1512), (6, 1260),
       (7, 1080), (8, 945), (9, 840), (10, 756), (12, 630), (14, 540),
       (15, 504), (18, 420), (20, 378), (21, 360), (24, 315), (27, 280),
       (28, 270), (30, 252), (35, 216), (36, 210), (40, 189), (42, 180),
       (45, 168), (54, 140), (56, 135), (60, 126), (63, 120), (70, 108),
       (72, 105), (84, 90)]
      

      We investigated finding the smallest number with a given number of divisors in Teaser 2580, so if we want to look for the smallest number with exactly 64 divisors (which may have been the setters intention), we can find the answer a little faster.

      The following runs in 72ms. (Internal runtime is 165µs).

      from enigma import (Accumulator, primes, rev, multiply, arg, printf)
      
      # number of divisors to find
      N = arg(64, 0, int)
      
      # find smallest number with exactly k divisors
      def solve(k):
        # consider factorisations of k
        r = Accumulator(fn=min)
        for ds in primes.factorisations(k):
          # pair the largest powers with the smallest primes
          n = multiply(p**(x - 1) for (p, x) in zip(primes, rev(ds)))
          r.accumulate(n)
        return r.value
      
      n = solve(N)
      printf("{N} divisors -> n = {n}")
      assert primes.tau(n) == N
      

      Like

  • Unknown's avatar

    Jim Randell 8:43 am on 17 October 2024 Permalink | Reply
    Tags:   

    Teaser 2603: Pandigital square 

    From The Sunday Times, 12th August 2012 [link] [link]

    I call my telephone number “pandigital”, in the sense that it is a nine-digit number using each of the digits 1 to 9. Amazingly, it is a perfect square. Furthermore, its square root is a five-digit number consisting of five consecutive digits in some order.

    It might interest you to know (although you do not need to) that my neighbour’s telephone number is also a nine-digit pandigital perfect square, but his is at least double mine.

    With a little logic and not many calculations you should be able to work out my telephone number.

    What is it?

    There are now 1100 Teaser puzzles available on the S2T2 site.

    [teaser2603]

     
    • Jim Randell's avatar

      Jim Randell 8:44 am on 17 October 2024 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run file executes in 92ms. (Internal runtime of the generated program is 15.4ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # consider phone numbers of the form ABCDEFGHI = sq(VWXYZ)
      --distinct="ABCDEFGHI,VWXYZ"
      --invalid="0,ABCDEFGHIV"
      
      # VWXYZ are consecutive digits (in some order) ...
      "is_consecutive(ordered(V, W, X, Y, Z))"
      
      # ... which give a square using each of the digits 1-9 exactly once
      "sq(VWXYZ) = ABCDEFGHI"
      
      --answer="ABCDEFGHI"
      

      Solution: The phone number is 157326849.

      Where:

      sqrt(157326849) = 12543

      There are 24 squares that can be formed from the digits 1-9 (each exactly once) that are at least twice this number.

      The smallest is: 326597184, and the largest is: 923187456.

      So we can suppose that the setter and his neighbour do not share an area code prefix in their phone numbers.


      Manually:

      A number consisting of the digits 1-9 has a digit sum of 45, and so must be divisible by 9, and so its square root must be divisible by 3.

      Using the additional information that the neighbour’s number is at least twice the setter’s number, we can see that the leading digit of the setter’s number must be 1-4, so the 5 digit root is less than 22334, which means it must start with 1 or 2, and so there are only a few possible consecutive digit sets that need to be considered, and only {1, 2, 3, 4, 5} will form numbers divisible by 3.

      So the candidates are: 12xxx, 13xxx, 14xxx, 15xxx, 21xxx. Where each xxx has 6 possibilities, which gives 30 possibilities.

      From these we can eliminate any candidates ending in 51, 52, 53, 235, 243, 245, 345, 423, 435, 532 (as the squares will include 0 or a repeated digit), which brings us down to 16 candidates.

      12 354 → 152621316
      12 534 → 157101156
      12 543 → 157326849 [*SOLUTION*]

      13 254 → 175668516
      13 425 → 180230625
      13 524 → 182898576
      13 542 → 183385764

      14 325 → 205205625
      14 523 → 210917529

      15 234 → 232074756
      15 324 → 234824976
      15 342 → 235376964
      15 432 → 238146624

      21 354 → 455993316
      21 534 → 463713156
      21 543 → 464100849

      And only one of these squares is a reordering of the digits 1-9.

      Like

      • ruudvanderham's avatar

        ruudvanderham 2:59 pm on 17 October 2024 Permalink | Reply

        Enumerating all 5 digit numbers with 5 consecutive numbers and then checking whether the square of that is pandigital:

        from istr import istr
        
        for i in range(1, 6):
            for digit5 in istr.concat(istr.permutations(range(i, i + 5))):
                square_of_digit5 = digit5 * digit5
                if len(square_of_digit5) == 9 and set(square_of_digit5) == set(istr.digits("1-9")):
                    print(digit5, square_of_digit5)
        

        Like

    • GeoffR's avatar

      GeoffR 12:44 pm on 17 October 2024 Permalink | Reply

      As Brian points out on his PuzzlingInPython site for this teaser, the first number is less than 5.10^8 so its square root is less than 22361 and the leading digit is 1 or 2. My neighbour’s telephone number is not needed for the solution.

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; var 1..9:D;
      var 1..9:E; var 1..9:F; var 1..9:G; var 1..9:H; var 1..9:I;
      constraint all_different([A,B,C,D,E,F,G,H,I]);
      
      % Telephone number is ABCDEFGHI
      var 100000000..500000000: tel_num == A*pow(10,8) + B*pow(10,7) + C*pow(10,6)
      + D*pow(10,5) + E*pow(10,4) + F*pow(10,3) + G*pow(10,2) + H*10 + I;
      
      % Square root of telephone number is abcde
      var 1..5:a; var 1..5:b; var 1..5:c; var 1..5:d; var 1..5:e;
      constraint all_different([a,b,c,d,e]);
      var 10000..99999: abcde ==  a*pow(10,4) + b*pow(10,3) + c*pow(10,2) + d*10 + e;
      
      constraint a ==1 \/ a == 2;
      constraint abcde * abcde == tel_num;
      
      solve satisfy;
      
      output ["Telephone number = " ++ show(tel_num) ++ "\n"
      ++ "Square root of tel. num. = " ++ show (abcde) ];
      
      % Telephone number = 157326849
      % Square root of tel. num. = 12543
      % ----------
      % ==========
      
      

      Like

    • GeoffR's avatar

      GeoffR 2:11 pm on 17 October 2024 Permalink | Reply

      
      # the first number is less than 5.10^8 so its square root
      # is less than 22361  and the leading digit is 1 or 2 
      # and 5 cconsecutive digits, in some order
      
      from itertools import permutations
      
      for p1 in permutations('12345'):
          a,b,c,d,e = p1
          if a not in ('12'):continue
          abcde = int(a + b + c + d + e)
          tel_num = abcde ** 2
          if tel_num > 5 * 10**8:continue
          if len(set(str(tel_num))) != 9 \
          or len(str(tel_num)) != len(set(str(tel_num))):
              continue
          print('Tel_num =', tel_num)
      
      

      Like

  • Unknown's avatar

    Jim Randell 11:26 am on 10 October 2024 Permalink | Reply
    Tags:   

    Teaser 2609: String art 

    From The Sunday Times, 23rd September 2012 [link] [link]

    Callum knocked some tacks onto the edge of a circular board. He then used pieces of string to make all possible straight-line connections between pairs of tacks. The tacks were arranged so that no three pieces of string crossed at the same point inside the circle. If he had used six tacks this would have required fifteen pieces of string and it would have created fifteen crossing points inside the circle. But he used more than six and in his case the number of crossing points inside the circle was a single-digit multiple of the number of pieces of string used.

    How many tacks did he use?

    [teaser2609]

     
    • Jim Randell's avatar

      Jim Randell 11:27 am on 10 October 2024 Permalink | Reply

      See also: Enigma 77, Enigma 1769.

      There is a line between each pair of tacks, so the number of lines is:

      L(n) = C(n, 2) = n(n − 1)/2

      Each crossing point is defined by two of the lines (any four different points form the vertices of a quadrilateral, the diagonals of which give the crossing lines), and each line is defined by two tacks.

      So the number of crossings is given by:

      X(n) = C(n, 4) = n(n − 1)(n − 2)(n − 3)/24

      And we are interested in when (for some single digit k):

      X(n) = k L(n)
      k = X(n) / L(n)

      12k = (n − 2)(n − 3)

      So we need to find two consecutive numbers (≥ 3), that give a suitable value for k = 2..9.

      The only candidate is k = 6:

      12 × 6 = 72 = 8 × 9

      Hence n = 11.

      Solution: Callum used 11 tacks.

      L(11) = 55
      X(11) = 330
      330 = 6 × 55

      The next solution would occur at n = 14.

      L(14) = 91
      X(14) = 1001
      1001 = 11 × 91

      but the multiple is not a single digit number.


      Here is a program that considers increasing n values:

      from enigma import (irange, inf, C, printf)
      
      # consider number of tacks
      for n in irange(7, inf):
        # calculate number of lines and number of crossings
        (L, X) = (C(n, 2), C(n, 4))
        (k, r) = divmod(X, L)
        if k > 9: break
        if r != 0: continue
        # output solution
        printf("n={n}: L={L} X={X}; k={k}")
      

      Alternatively here is a program that solves the equation:

      from enigma import (Polynomial, irange, printf)
      
      # make the polynomial p(n) = (n - 2)(n - 3)
      n = Polynomial('n', var='n')
      p = (n - 2) * (n - 3)
      
      # consider possible single digit multiples k
      for k in irange(2, 9):
        # find integer roots of the polynomial p(n) = 12k
        for r in p.find_roots(12 * k, domain='Z'):
          # look for values > 6
          if r > 6:
            # output solution
            printf("n={r} [k={k}]")
      

      Like

    • Ruud's avatar

      Ruud 7:29 am on 11 October 2024 Permalink | Reply

      import math
      import itertools
      
      for number_of_tacks in itertools.count(7):
          number_of_crossing_points = math.comb(number_of_tacks, 4)
          number_of_pieces_of_string = math.comb(number_of_tacks, 2)
          if number_of_crossing_points / number_of_pieces_of_string > 9:
              break
          if number_of_crossing_points % number_of_pieces_of_string == 0:
              print(f"{number_of_tacks=}  {number_of_pieces_of_string=}  {number_of_crossing_points=}  {number_of_crossing_points//number_of_pieces_of_string=}")
      

      Like

  • Unknown's avatar

    Jim Randell 11:10 am on 8 October 2024 Permalink | Reply
    Tags:   

    Teaser 2580: Hard times 

    From The Sunday Times, 4th March 2012 [link] [link]

    Penny found a multiplication table in the back of one of Joe’s old school books – the top corner of it is illustrated. She noticed that prime numbers only appeared twice in the body of the table, whereas 4 (for example) appeared 3 times and 12 appeared 6 times. Penny could not help wondering how many times large numbers appeared in a huge table.

    What is the smallest number that will appear 250 times?

    Note: In this puzzle we are looking for exact numbers of appearances (rather than minimum number of appearances).

    [teaser2580]

     
    • Jim Randell's avatar

      Jim Randell 11:11 am on 8 October 2024 Permalink | Reply

      See also: Teaser 11.

      Here is a straightforward, but slow solution. It runs in 7.6s (using PyPy):

      from enigma import (irange, inf, tau, arg, printf)
      
      N = arg(250, 0, int)
      
      for n in irange(1, inf):
        if tau(n) == N:
          printf("{N} divisors -> n = {n}")
          break
      

      Solution: The first number to appear exactly 250 times is: 5670000.

      5670000 = (2^4)(3^4)(5^4)(7)

      Although this is not the first number to appear at least 250 times. That is: 1081080, which appears 256 times.

      1081080 = (2^3)(3^3)(5)(7)(11)(13)


      However, with a bit more work we can come up with a much faster program:

      If n has divisors d1, …, dk then it will appear at the following positions in the table:

      d1 × d1
      d2 × d2

      dk × dk

      (where dj = n / dj).

      So n appears k times if it has k divisors.

      And if a number is expressed as a product of its prime factors:

      n = (p1^e1) × (p2^e2) × … × (pk^ek) = ∏(pj^ej)

      Then each prime pj can appear 0 to ej times, hence the number of different divisors is:

      tau(n) = (e1 + 1) × (e2 + 1) × … × (ek + 1) = ∏(ej + 1)

      We are looking for a number with exactly 250 divisors, so we can look at possible factorisations of 250.

      So, for example, if we factorise 250 as:

      250 = a × b × c

      Then any number of the form:

      p1^(a − 1) × p2^(b − 1) × p3^(c − 1)

      (for primes p1, p2, p3) will have exactly 250 divisors.

      So, by considering the possible factorisations of 250, and using the smallest primes, we can find the smallest candidate number for each factorisation, and then choose the smallest of the candidate numbers found. (Often, but not always, the smallest number is given by the prime factorisation of the original number).

      This Python program runs in 65ms, and has an internal runtime of 205µs.

      from enigma import (Accumulator, primes, trim, rev, multiply, arg, printf)
      
      # number of divisors to find
      N = arg(250, 0, int)
      
      # find factorisations of <n> using divisors <ds>
      def _factorisations(n, ds, i, ss=[]):
        # are we done?
        if n == 1:
          yield tuple(ss)
        else:
          # look for the next divisor
          while i >= 0:
            d = ds[i]
            if n >= d:
              (r, x) = divmod(n, d)
              if x == 0:
                yield from _factorisations(r, ds, i, [d] + ss)
            i -= 1
      
      # find factorisations of <n> (aka multiplicative partitions)
      def factorisations(n):
        # always return the trivial factorisation
        yield (n,)
        if n < 4: return
        # look for other factorisations using divisors (other than 1 and n)
        ds = trim(primes.divisors(n), head=1, tail=1)
        yield from _factorisations(n, ds, len(ds) - 1)
      
      # find smallest number with k divisors
      def solve(k):
        # consider factorisations of k
        r = Accumulator(fn=min)
        for ds in factorisations(k):
          # pair the largest powers with the smallest primes
          n = multiply(p**(x - 1) for (p, x) in zip(primes, rev(ds)))
          r.accumulate(n)
        return r.value
      
      n = solve(N)
      printf("{N} divisors -> n = {n}")
      assert primes.tau(n) == N
      

      The [[ factorisations() ]] function will appear in the next release of the enigma.py library.

      Like

  • Unknown's avatar

    Jim Randell 9:16 am on 3 October 2024 Permalink | Reply
    Tags:   

    Teaser 2599: Removing a card 

    From The Sunday Times, 15th July 2012 [link] [link]

    I have taken a set of cards and written a different digit on each. Overall I have used a set of consecutive digits. Then I have arranged the cards in some order to make one large number. This number is divisible by the digit that is one more than the number of cards.

    Reading from left to right, if any card is removed then the remaining number is divisible by the position of the card removed. For example, if the card in the sixth position is removed and the remaining cards are pushed together to form another number, then that number is divisible by 6.

    What was the original number?

    [teaser2599]

     
    • Jim Randell's avatar

      Jim Randell 9:16 am on 3 October 2024 Permalink | Reply

      Note that the number formed from the cards “is divisible by the digit that is one more than the number of cards”.

      If there are k cards, then (k + 1) must be a single digit, i.e.:

      k + 1 ≤ 9
      ⇒ k ≤ 8

      And the example given involves removing the 6th card and closing up the gap, hence:

      k > 6
      ⇒ k ≥ 7

      So the only possible values for k are 7 or 8.

      This Python program runs in 228ms. (Internal runtime is 142ms).

      from enigma import (irange, tuples, subsets, nconcat, delete, printf)
      
      # solve the puzzle for digits <ds>
      def solve(ds):
        k = len(ds)
        # divisibility by 3 or 9 does not depend on order
        if (k == 2 or k == 8) and sum(ds) % (k + 1) > 0: return
        # choose an ordering for the digits
        for ss in subsets(ds, size=k, select='P'):
          if ss[0] == 0: continue
          n = nconcat(ss)
          # the number is divisible by one more than the number of digits
          if n % (k + 1) > 0: continue
          # if the digit at position i is removed, the number formed from
          # the remaining digits is divisible by i
          if any(nconcat(delete(ss, [i - 1])) % i > 0 for i in irange(2, k)): continue
          # output solution
          printf("k={k}: {ds} -> {ss} -> {n}")
      
      digits = list(irange(0, 9))
      
      # the puzzle implies there between 7 and 8 cards
      for k in irange(7, 8):
        # choose k consecutive digits
        for ds in tuples(digits, k):
          solve(ds)
      

      Solution: The original number was: 2435160.


      If we allow the full range numbers of cards (2 to 9) there are the following solutions:

      k=2: 21, 45, 87
      k=3: 120, 456, 576, 756, 876
      k=5: 14352, 41352, 47658, 74658, 78654, 87654
      k=6: 513240
      k=7: 2435160
      k=9: 143756820, 156473280, 173856420, 176583240, 253716840, 273156480, 453716820, 476513280, 513276840, 516783240, 543176820, 573126840, 573416820, 586713240, 713526480, 716543280, 743516820, 746153280, 753216480, 756183240, 756813240, 873156420, 876513240

      Note that beyond k=2 the numbers are even (as removing the 2nd digit must result in a number divisible by 2, and so the final digit must be even).

      And beyond k=5 the numbers end in 0 (as removing the 5th digit must result in a number divisible by 5, and so the final digit must be 0). And so the only consecutive sequence of digits is 0..(k − 1).

      This means that for k=8 the set of digits is 0..7, but these digits have a sum of 28, which is not divisible by 9, and so none of the arrangements of digits will be. So there is no possible collection of 8 cards.

      We know then, that the number must be formed from 7 cards, using the digits 0..6.

      The following run file uses these restrictions and divisibility rules on the 7-digit number, and has an internal run time of 275µs.

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # k=7: n = ABCDEFG
      --digits="0-6"
      
      # the number is divisible by 8
      "ABCDEFG % 8 = 0"
      
      # removing the digit at position i gives a number divisible by (i + 1)
      "ACDEFG % 2 = 0"
      "ABDEFG % 3 = 0"
      "ABCEFG % 4 = 0"
      "ABCDFG % 5 = 0"
      "ABCDEG % 6 = 0"
      "ABCDEF % 7 = 0"
      
      # additionally ...
      # G must be 0
      --assign="G,0"
      # ABDEFG is divisible by 3 -> C is divisible by 3
      --invalid="1|2|4|5,C"
      # ABCEFG is divisible by 4 -> F is divisible by 2
      --invalid="1|3|5,F"
      
      --answer="ABCDEFG"
      

      Like

  • Unknown's avatar

    Jim Randell 9:19 am on 27 September 2024 Permalink | Reply
    Tags:   

    Teaser 2606: Cue for a queue 

    From The Sunday Times, 2nd September 2012 [link] [link]

    Alan, Brian, Colin, Dave and Ed have surnames Smith, Jones, Rogers, Mason and Hall, but not in that order. They form themselves into a queue. Brian is somewhere ahead of Mr Smith who is somewhere ahead of Ed. Similarly, Mr Jones is ahead of Colin who is ahead of Dave who is ahead of Mr Hall. Mr Mason’s two neighbours in the queue are Alan and Mr Rogers.

    If I told you Alan’s surname it would now be possible to work our all their surnames and their positions in the queue.

    What is Alan’s surname and what is his position in the queue?

    [teaser2606]

     
    • Jim Randell's avatar

      Jim Randell 9:20 am on 27 September 2024 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to assign positions 1-5 in the queue to the forenames and surnames, such that the required conditions are met.

      We can then use the [[ filter_unique() ]] function to select solutions from the viable assignments, such that knowing Alan’s surname gives a single solution for all names.

      The following Python program runs in 89ms. (Internal runtime is 5.8ms).

      from enigma import (SubstitutedExpression, trim, filter_unique, update, peek, join, printf)
      
      # generate possible queues
      def queues():
        # allocate positions 1-5 in the queue to:
        #
        #  Alan = A; Brian = B; Colin = C; Dave = D; Ed = E
        #  Smith = S; Jones = J; Rogers = R; Mason = M; Hall = H
        #
        p = SubstitutedExpression(
          [
            # the surnames are not given in the correct order
            "A != S or B != J or C != R or D != M or E != H",
            # B is ahead of S, who is ahead of E
            "B < S", "S < E",
            # J is ahead of C, who is ahead of D, who is ahead of H
            "J < C", "C < D", "D < H",
            # M's neighbours (on different sides) are A and R
            "abs(A - R) = 2", "abs(A - M) = 1", "abs(R - M) = 1",
          ],
          base=6,
          digits=[1, 2, 3, 4, 5],
          distinct=["ABCDE", "SJRMH"],
        )
      
        # solve the puzzle
        for s in p.solve(verbose=0):
          # assemble the queue
          q = ["??"] * 6
          for k in "ABCDE": q[s[k]] = update(q[s[k]], [0], k)
          for k in "SJRMH": q[s[k]] = update(q[s[k]], [1], k)
          # return the queue
          yield trim(q, head=1)
      
      # knowing A's surname gives a unique solution
      f = (lambda q: peek(x for x in q if x[0] == 'A'))
      for q in filter_unique(queues(), f).unique:
        printf("{q}", q=join(q, sep=" "))
      

      Solution: Alan Smith is second in the queue.

      The queue is as follows:

      1st = Brian Jones
      2nd = Alan Smith
      3rd = Colin Mason
      4th = Dave Rogers
      5th = Ed Hall

      There are 5 candidate solutions:

      BJ, AS, CM, DR, EH
      BJ, CS, ER, DM, AH
      BJ, CS, DR, EM, AH
      AJ, BM, CR, DS, EH
      AJ, CM, BR, DS, EH

      The first of these is unique for AS. The next two both have AH, and the final two have AJ.

      Like

      • ruudvanderham's avatar

        ruudvanderham 5:39 pm on 27 September 2024 Permalink | Reply

        import itertools
        
        for firstnames, lastnames in zip(itertools.repeat("Alan Brian Colin Dave Ed".split()), itertools.permutations("Smith Jones Rogers Mason Hall".split(), r=5)):
            for positions in itertools.permutations(range(1, 6)):
                position = {firstname: position for firstname, position in zip(firstnames, positions)}
                position.update({lastname: position for lastname, position in zip(lastnames, positions)})
                if position["Brian"] < position["Smith"] < position["Ed"]:
                    if position["Jones"] < position["Colin"] < position["Dave"] < position["Hall"]:
                        if {position["Mason"] - position["Alan"], position["Mason"] - position["Rogers"]} == {-1, 1}:
                            if list(lastnames) != "Smith Jones Rogers Mason Hall".split():
                                for firstname, lastname in zip(firstnames, lastnames):
                                    print(f"{firstname+ " "+lastname:12} {position[firstname]}  ", end="")
                                print()
        

        This prints:

        Alan Smith   2  Brian Jones  1  Colin Mason  3  Dave Rogers  4  Ed Hall      5  
        Alan Jones   1  Brian Rogers 3  Colin Mason  2  Dave Smith   4  Ed Hall      5
        Alan Jones   1  Brian Mason  2  Colin Rogers 3  Dave Smith   4  Ed Hall      5
        Alan Hall    5  Brian Jones  1  Colin Smith  2  Dave Rogers  3  Ed Mason     4  
        Alan Hall    5  Brian Jones  1  Colin Smith  2  Dave Mason   4  Ed Rogers    3
        

        So, it has to be Alan Smith (the others are not unique) and he is in second position.

        Like

  • Unknown's avatar

    Jim Randell 10:43 am on 17 September 2024 Permalink | Reply
    Tags:   

    Teaser 2604: Foreign friends 

    From The Sunday Times, 19th August 2012 [link] [link]

    The telephone number of my Japanese friend is a ten-digit number written as a group of five two-digit numbers. The phone number does not start with a 0, and no digit is used more than once. The numbers in the group are in increasing order, no two of them have a common factor greater than 1, and one of them is a fourth power. Also, the product of the numbers in the group is divisible by four of the first five primes.

    The telephone number of my Russian friend is also a ten-digit number but it is written as a group of two five-digit numbers. The number and group have the same properties as those stated above. The second digit of the Russian number is double the second digit of the Japanese number.

    What are the two telephone numbers?

    [teaser2604]

     
    • Jim Randell's avatar

      Jim Randell 10:44 am on 17 September 2024 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      It runs in 772ms. (Internal runtime is 582ms, although this can be reduced to 377ms by splitting out the [[ is_coprime(@jps) ]] check into 10 separate coprime tests (one for each pair)).

      Note: An up-to-date version of enigma.py is required for the multiple argument version of is_coprime().

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # Japanese phone number = AB CD EF GH IJ
      # Russian phone number = KLMNP QRSTU
      --distinct="ABCDEFGHIJ,KLMNPQRSTU"
      --macro="@jps = AB, CD, EF, GH, IJ"
      --macro="@rus = KLMNP, QRSTU"
      
      # neither phone number starts with 0
      --invalid="0,AK"
      
      # in the groups ...
      # the numbers are in increasing order
      "A < C" "C < E" "E < G" "G < I"
      "K < Q"
      
      # the numbers are pairwise co-prime
      "is_coprime(@jps)"
      "is_coprime(@rus)"
      
      # use "at least" or "exactly" for counting?
      --code="count = icount_at_least"  # or: icount_exactly
      
      # one of them is a fourth power
      --code="pow4s = set(i**4 for i in irange(0, 17))"
      --code="pow4 = lambda ns: count(ns, is_in(pow4s), 1)"
      "pow4([@jps])"
      "pow4([@rus])"
      
      # the product is divisible by four of {2, 3, 5, 7, 11}
      --code="_divs = (lambda n, ds, k: count(ds, (lambda d: n % d == 0), k))"
      --code="divs = (lambda ns: _divs(multiply(ns), [2, 3, 5, 7, 11], 4))"
      "divs([@jps])"
      "divs([@rus])"
      
      # 2nd digit of the Russian number is double the 2nd digit of the Japanese number
      "2 * B = L"
      --invalid="5|6|7|8|9,B"  # so, B < 5
      
      --template="(AB CD EF GH IJ) (KLMNP QRSTU)"
      --solution=""
      --denest=-1
      

      Solution: The numbers are: (23 49 50 67 81) and (46970 83521).

      The individual parts factorise as:

      (23 49 50 67 81) = ([23] [7×7] [2×5×5] [67] [3×3×3×3])
      (46970 83521) = ([2×5×7×11×61] [17×17×17×17])

      From which we see in each number there are no prime factors common to any two of the parts, so each is pairwise coprime.

      And the final part in each is the fourth power (of a prime).

      The top number has divisors of: 2, 3, 5, 7 (but not 11).

      The bottom number has divisors of: 2, 5, 7, 11 (but not 3).

      Like

      • Ruud's avatar

        Ruud 12:22 pm on 19 September 2024 Permalink | Reply

        Brute force:

        from istr import istr
        import math
        import functools
        import operator
        
        istr.repr_mode("str")
        solutions = {2: set(), 5: set()}
        
        for s in istr.permutations(istr.range(10)):
            if s[0] != 0:
                for k in (2, 5):
                    numbers = tuple(istr("".join(s[i : i + k])) for i in range(0, len(s), k))
                    if all(number0 < number1 for number0, number1 in zip(numbers, numbers[1:])):
                        if sum((float(number) ** (1 / 4)).is_integer() for number in numbers) == 1:
                            if sum(functools.reduce(operator.mul, numbers) % prime == 0 for prime in (2, 3, 5, 7, 11)) == 4:
                                if all(math.gcd(int(number0), int(number1)) == 1 for number0, number1 in istr.combinations(numbers, 2)):
                                    solutions[k].add(istr(" ").join(numbers))
        for japanese in solutions[2]:
            for russian in solutions[5]:
                if japanese[1] * 2 == russian[1]:
                    print(f"{japanese=} {russian=}")
        

        Like

    • GeoffR's avatar

      GeoffR 4:41 pm on 19 September 2024 Permalink | Reply

      # Japanese telephone number is AB CD EF GH IJ
      # Russian telephone number is KLMNP QRSTU
      
      from itertools import permutations
      from enigma import is_coprime
      
      # max 5 digits for the 4th power
      pow4 = [n * n * n * n for n in range(2, 18)]
      
      dgts = set(range(10))
      
      # Japanese Telephone Number
      for p1 in permutations(dgts, 4):
        jp_found = 0
        A, B, C, D = p1
        if 0 in (A, C): continue
        # Constraint on 2nd digit of 1st Tel No. i.e. L = 2 *  B
        if B == 0 or B > 4: continue
        if not A < C: continue
        AB, CD = 10*A + B, 10*C + D
        if not is_coprime(AB, CD): continue
      
        # Find remaining digits in Japanese Telephone Number
        q1 = dgts.difference({A, B, C, D})
        for p2 in permutations(q1):
          E, F, G, H, I, J = p2
          if 0 in (E, G, I): continue
          if not A < C < E < G < I: continue
          EF, GH, IJ = 10*E + F, 10*G + H, 10*I + J
          
          # check ten co-prime conditions 
          if is_coprime(CD, EF) and is_coprime(EF, GH) and is_coprime(GH, IJ) \
            and is_coprime(AB, EF) and is_coprime(AB, GH) and is_coprime(AB, IJ) \
            and is_coprime(CD, GH ) and is_coprime(CD, IJ) and is_coprime(EF, IJ):
              
            # Check for a fourth power
            if any (x in pow4 for x in (AB, CD, EF, GH, IJ)):
              # Find number of divisors of Japanese Tel. No.
              cnt = 0
              for num in (AB, CD, EF, GH, IJ):
                for p3 in (2, 3, 5, 7, 11):
                  if num % p3 == 0:
                    cnt += 1
                if cnt == 4:
                  print(f"Japanese No. is {AB} {CD} {EF} {GH} {IJ}.")
                  jp_found = 1
      
                # Russian Telephone Number - find first half of Tel. No.
                for p5 in permutations(dgts,5):
                  K, L, M, N, P = p5
                  if K == 0: continue
                  if L != 2 * B: continue
                  KLMNP = 10000*K + 1000*L + 100*M + 10*N + P
                  # Assume 4th power is QRSTU  
                  if KLMNP not in pow4:
                    cnt2 = 0
                    for p5 in (2, 3, 5, 7, 11):
                      if KLMNP % p5 == 0:
                        cnt2 += 1
                  if cnt2 == 4:
                      
                    # Find second half of Russian Tel. No.
                    q6 = dgts.difference({K, L, M, N, P})
                    for p7 in permutations(q6):
                      Q, R, S, T, U = p7
                      QRSTU = 10000*Q + 1000*R + 100*S + 10*T + U
                      if Q == 0: continue
                      if not KLMNP < QRSTU: continue
                      if not is_coprime(KLMNP, QRSTU): continue
                      if QRSTU in pow4 and jp_found == 1:
                        print(f"Russian No. is {KLMNP} {QRSTU}.")
      
      #Japanese No. 23 49 50 67 81
      # Russian No. is 46970 83521
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 11:11 am on 29 August 2024 Permalink | Reply
    Tags: by: Simon Massarella   

    Teaser 2592: Inventive inventories 

    From The Sunday Times, 27th May 2012 [link] [link]

    It is known that there is just one 10-digit number that is “self-counting” in the sense that its first digit equals the number of 1s in it, the second digit equals the number of 2s in it, and so on, with the ninth digit equalling the number of 9s in it and the tenth digit equalling the number of 0s in it.

    Similarly, a 9-digit number is “self-counting” if its first digit equals the number of 1s in it, the second digit equals the number of 2s in it, and so on, with the ninth digit equalling the number of 9s in it (and with the 0s remaining uncounted).

    (a) What is the 10-digit self-counting number?
    (b) What is the highest 9-figure self-counting number?

    [teaser2592]

     
    • Jim Randell's avatar

      Jim Randell 11:12 am on 29 August 2024 Permalink | Reply

      This is puzzle is a variation on autobiographical numbers (or curious numbers), see: Puzzle #16, Enigma 476.

      Except in autobiographical numbers the digits (in order) usually count the number 0’s, then then numbers of 1’s, 2’s, etc.

      The following Python program runs in 386ms. (Internal runtime is 309ms).

      from enigma import (irange, decompose, join, printf)
      
      # generate self counting sequences
      def self_counting(k):
        js = (1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
        assert k == 10 or k in js
        # t of the k digits are counted
        for t in (irange(0, k) if k < 10 else [10]):
          # decompose the t digits into the k counted slots
          for ns in decompose(t, k, increasing=0, sep=0, min_v=0):
            # check the counting works
            if all(ns.count(j) == n for (j, n) in zip(js, ns)):
              yield ns
      
      # find length 10 and 9 self counting numbers
      for k in [10, 9]:
        for ns in self_counting(k):
          if (ns[0] == 0 and len(ns) > 1): continue  # skip leading zeros
          printf("[{k}] {ns}", ns=join(ns))
      

      Solution: (a) 2100010006; (b) 100000000.

      There is an additional self-counting sequence of size 9 and that is (0, 0, 0, 0, 0, 0, 0, 0, 0), but that does not give a viable 9-digit self-counting number (and even if it did we are looking for largest 9-digit self-counting number).

      The full list of self-counting sequences:

      1: 0, 1
      2: 00, 10
      3: 000, 100
      4: 0000, 1000
      5: 00000, 10000
      6: 000000, 100000
      7: 0000000, 1000000
      8: 00000000, 10000000
      9: 000000000, 100000000
      10: 2100010006

      If we bring the 0 to the front of the [[ js ]] array (at line 5), then we get the more usual base 10 autobiographical numbers.

      4: 1210, 2020
      5: 21200
      7: 3211000
      8: 42101000
      9: 521001000
      10: 6210001000

      And we see for the 10-digit self-counting number (where each of the digits is counted) we can calculate the 10-digit autobiographical number and move the leading digit to the end.

      Like

  • Unknown's avatar

    Jim Randell 10:46 am on 15 August 2024 Permalink | Reply
    Tags:   

    Teaser 2623: Latecomer 

    From The Sunday Times, 30th December 2012 [link] [link]

    Imogen is having a New Year party tomorrow night and she has invited Madeline, hoping not to have a repeat of last year’s episode which completely spoilt the celebrations. Last New Year’s Eve Madeline had been due at a certain whole number of minutes past an hour but she was late, the number of minutes late being that same aforementioned “certain number of minutes” but with the digits in the reverse order. Madeline pointed out that at least the angle between the hands of the clock at the moment of her arrival was the same as it would have been when she was due.

    At what time was she due to arrive?

    [teaser2623]

     
    • Jim Randell's avatar

      Jim Randell 10:46 am on 15 August 2024 Permalink | Reply

      Madeline was expected to arrive at a time of xy minutes past a specified hour, but she was late, and arrived yx minutes later than her expected time. So yx (and xy) cannot be 00 (as then she would not be late).

      This Python program finds both possible solutions to the puzzle.

      It runs in 74ms. (Internal runtime is 3.6ms).

      Run: [ @replit ]

      from enigma import (Rational, irange, cproduct, nrev, printf)
      
      Q = Rational()
      
      # calculate angle between hands at time h:m
      # angles are between 0 (coincident) and 1 (opposite)
      def angle(h, m):
        (x, m) = divmod(m, 60)
        h = (h + x) % 12
        ah = Q(h + Q(m, 60), 6)
        am = Q(m, 30)
        a = abs(ah - am)
        return (2 - a if a > 1 else a)
      
      # consider possible scheduled arrival times
      for (h, m) in cproduct([irange(0, 11), irange(1, 59)]):
        a1 = angle(h, m)
        # calculate angle at actual arrival time
        a2 = angle(h, m + nrev(m, 2))
        # are angles the same?
        if a1 == a2:
          # output solution
          printf("expected arrival = {h:02d}:{m:02d} [angle = {a:.2f} min]", a=float(a1 * 30))
      

      There are two possible answers:

      Expected time = 11:43; Actual time = 11:43 + 34 minutes = 12:17
      Expected time = 5:43; Actual time = 5:43 + 34 minutes = 6:17

      Both of which are illustrated below, with the same shape indicating the identical angles between the hands:

      As the party in question is a New Years Eve party, presumably the setter intends us to choose the solution where Madeline was expected to arrive before midnight, but actually arrived after midnight (although this could easily have been specified in the problem text).

      Solution: Madeline was due to arrive at 11:43 pm.

      Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Design a site like this with WordPress.com
Get started