1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
from itertools import combinations, product # marble radius on table R, on top r, pyramid height h # # h = R + r + sqrt((R + r)^2 - (4/3) * R^2) # # with r > (2 / 3^(1/2) - 1).R and 2.R <= h <= 30 # map R to lists of maps of r to h dR2ldr2h = {} for R in range(1, 16): dr2h = {} for r in range(1, 16): if r <= (2 / 3 ** (1 / 2) - 1) * R: continue s2, _r = divmod(3 * (R + r) ** 2 - 4 * R * R, 3) if not _r and (s := round(s2 ** (1/2))) ** 2 == s2: if 2 * R <= (h := R + r + s) <= 30: dr2h[r] = h if dr2h: dR2ldr2h[R] = dR2ldr2h.get(R, []) + [dr2h] # collect all possible marble radii mr = set(dR2ldr2h.keys()) for l_r2h in dR2ldr2h.values(): mr.update(*(r2h for r2h in l_r2h)) # consider subsets of marble radii of size three or more d_h2r = {} for nm in range(3, len (mr) + 1): for rds in combinations(mr, nm): hts = [] # collect pyramid heights for all different marble pairings for a, b in combinations(rds, 2): for a, b in ((a, b), (b, a)): if a in dR2ldr2h: for dr2h in dR2ldr2h[a]: if b in dr2h: hts.append(dr2h[b]) # record all marble sets that can make three or more # different height pyramids by mapping the pyramid # height groups to the marble sets that make them if hts and len(hts) == len(set(hts)) >= 3: k = tuple(sorted(hts)) d_h2r[k] = d_h2r.get(k, set()) | {rds} p = lambda t, f=2: ' '.join(f"{x:{f}}" for x in t) # consider pairs of pyramid height groups for Jack and Jill for a, b in combinations(d_h2r.keys(), 2): # look for two height groups with disjoint marble radii for ar, br in product(d_h2r[a], d_h2r[b]): if not (set(ar) & set(br)): print(f"Marble radii [{p(sorted(ar + br), 1)}]\n" f" marble radii -> pyramids\n" f"({p(ar)}) -> ({p(a)})\n({p(br)}) -> ({p(b)})") |

This is a somewhat surprising teaser since it turns out that there are six combinations of the three base marble radii (R) and the upper marble radii (r) that yield heights but only one of these is a true tetrahedron. In the other five, the tops of all four marbles are at the same height with the resulting ‘tetrahedra’ being no more than flat planes!

]]>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
def solve_puzzle(): original_perimeter = 0 polygon_perimeter = 0 polygon_area = 0 # Iterate through all possible dimensions of the rectangular card for width in range(1, 50): for height in range(1, 50): original_perimeter = 2 * (width + height) # Calculate the lengths of the sides of the resulting polygon side1 = width side2 = height side3 = ((width ** 2) + (height ** 2)) ** 0.5 # Calculate the perimeter of the resulting polygon polygon_perimeter = side1 + side2 + side3 # Check if the perimeters satisfy the given condition if polygon_perimeter == original_perimeter - 5/28: # Calculate the area of the polygon semi_perimeter = polygon_perimeter / 2 polygon_area = (semi_perimeter * (semi_perimeter - side1) * (semi_perimeter - side2) * (semi_perimeter - side3)) ** 0.5 return polygon_area return None # Call the function to solve the puzzle result = solve_puzzle() # Print the area of the polygon if a solution is found if result: print(f"The area of the polygon is: {result} square centimeters") else: print("No solution found.") |

Jim Randell already has used Pythagorean triples. Luckily I found a different approach (divisors).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# L1 = (-h/w)x + h # line L1 passes through 2 corners (w, 0) and (0, h) # L2 = (w/h)x + h/2 - w**2/(2h) # both lines are perpendicular to each other and cross at (w/2, h/2) # new triangle has two sides of length <w>, 2 sides of length <o> # and one side of length <x> for w in range(1, 49): # height is longer than width w2 = w * w # only check for divisors of w*w because of x2 formula for h in [x for x in range(w + 1, 50) if w2 % x == 0]: # consider triangle with sides x, w and (h - 2 * o) #x2 = (h - 2o)**2 + w2 #x2 = ((h2 - 2oh) / h)**2 + w2 # use that 2oh equals h2 - w2 (see below) #x2 = ((h2 - (h2 - w2)) / h)**2 + w2 x2 = (w2 / h)**2 + w2 # x2 has to be square if (x := x2**.5) % 1: continue # calculate side <o> which occurs twice # o equals L2(0) which is h/2 - w**2/(2h) o, r = divmod(h * h - w2, 2 * h) if r: continue # new perimeter is five twenty-eighths smaller than the old perimeter O = 2 * (h + w) N = 2 * (w + o) + x if 23 * O != 28 * N: continue # area is wo + (h-2o)w/2 + wo/2 A = w * (h + o) / 2 print("answer:", A, "cm^2") |

@Brian, when I use (without even calling divisors()):

1 2 3 |
from number_theory import divisors |

the program runs considerably slower . This doesn’t happen when I import divisors from enigma.

]]>The rectangle has sides of length a and b and its diagonal is of length c; the fold line, which crosses the diagonal at its mid-point and is perpendicular to it, is of length d. The relationships between the various lengths can be derived using similar triangles. Since the fold line has an integer length, we know that the diagonal must be rational which means that (a, b, c) a Pythagorean triple.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# the height of the rectangle for a in range(1, 50): # the width of the rectangle for b in range(a, 50): # compute the length of the diagonal c = (a * a + b * b) ** (1 / 2) # the length of the fold line (an integer) d = a * c / b if round(d) == d: # the smallest side of the polygon (also an integer) s = (b * b - a * a) / (2 * b) if round(s) == s: # the perimeter condition (redundant) p1, p2 = 2 * (a + b), 2 * (a + s) + d if 23 * p1 == 28 * p2: area = a * b / 2 + a * (b * b - a * a) / (4 * b) print(f"Area = {area}cm^2 (rectangle: {a}cm x {b}cm).") |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from itertools import combinations, permutations lst = [] # for all combinations of three different digits tup6 = tuple(''.join(x) for x in permutations("abc", 3)) # make extraction strings lst += [y + ' - ' + x if y > x else x + ' - ' + y for x, y in combinations(tup6, 2)] # build dictionary of impact on total sum per letter d = {k: [10**(2 - df.index(k)) - 10**(2 - df[6:].index(k)) for df in lst] for k in "abc"} total = ' + '.join(str(sum(vs)) + k for k, vs in d.items() if sum(vs)) print(total) |

with output:

-774a + 774c

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# analysis shows that the sum of the absolute differences # between all pairs of the six numbers formed from the # digits a, b and c (with a < b < c) is 774 * (c - a) # so for any 3-digit number number with different digits the total sum # is 774 * (highest digit minus lowest digit) # 774 = 2 * 3 * 3 * 43 # k * 774 must be divisible by nbr (k 1..9, 258 < nbr < 1000) # so nbr should be at least divisible by 43 # which higher three-figure number has the same property? for nbr in range(258 + 43, 1000, 43): # three different digits if len(set(str(nbr))) != 3: continue # determine highest digit and lowest digit ld, hd = int((n1 := sorted(str(nbr)))[0]), int(n1[-1]) # sum of the absolute differences between all pairs must be divisible # by the requestd number if 774 * (hd - ld) % nbr: continue print('answer =', nbr) |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# age range for numberer for age in range(1, 100): # create the house numbers he used as a list hns = [x % 100 for x in range(age, 100 * age, age)] # check that all numbers (1 .. 99) are used if 0 not in hns and len(set(hns)) == 99: # now find a house whose number and 'normal' number are the same # it should be a 2-digit number that can be reversed to a different number # luckily i-boundary 99 will be rejected (no IndexError for hns[i]) sol = [hn for i, hn in enumerate(hns[9:], 10) if i == hn and all(hn % j for j in (10, 11)) and # must be reversable int(str(hn)[::-1]) in {hns[i - 2], hns[i]}] for s in sol: print(f"House number is {s}, numberer's age is {age}") |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
from math import log from sys import float_info eps = float_info.epsilon sign = lambda x: -1 if x < 0 else x > 0 # ITP "Interpolate, Truncate and Project" method root finder # (based on the description on Wikipedia) def itp_solve(fn, a, b, e=eps, k1=0.1, k2=2, n0=1): n_h = log((b - a) / (2 * e), 2) n_m = n_h + n0 j = 0 ya, yb = fn(a), fn(b) assert ya < 0 < yb, "incorrect values for a and b" assert k1 >= 0 and 1 <= k2 <= 1.5 + 5**.5 / 2, \ "incorrect values for k1 and k2" while(b - a > 2 * e): # calculating parameters x_h = (a + b) / 2 r = e * 2**(n_m - j) - (b - a) / 2 d = k1 * (b - a)**k2 # interpolation x_f = (yb * a - ya * b) / (yb - ya) # truncation s = sign(x_h - x_f) x_t = x_f + s * d if d <= abs(x_h - x_f) else x_h # projection x_ITP = x_t if abs(x_t - x_h) <= r else x_h - s * r # updating interval y_ITP = fn(x_ITP) if y_ITP > 0: b, yb = x_ITP, y_ITP elif y_ITP < 0: a, ya = x_ITP, y_ITP else: a, b = x_ITP, x_ITP j += 1 return (a + b) / 2 def dif(x): # the distance going due south and then due east d1 = 2 * x # the distance going diagonally after crossing the river d2 = 5 + (x ** 2 + (x - 5) ** 2) ** 0.5 # return the squared difference of (3/4) d1 and d2 return (0.75 * d1 - d2) x = itp_solve(dif, 0, 100) print('shorter distance = {:.2f} metres'.format(1.5 * x)) |

1 2 3 4 5 |
def dif(x): return (x - 1.7)**17 - 2.45 + 3.7 * (3.1 - x)**9 x, i = bm_solve(dif, -1, 100) |

Chandrupatla gives the correct x = 2.7541183874033877

]]>