Project Euler Problem 33: Digit Cancelling Fractions¶
The source code for this problem can be found here.
Problem Statement¶
The fraction \(\frac{49}{98}\) is a curious fraction, as an inexperienced mathematician in attempting to simplify it may incorrectly believe that \(\frac{49}{98} = \frac{4}{8}\), which is correct, is obtained by cancelling the \(9\mbox{s}\).
We shall consider fractions like, \(\frac{30}{50} = \frac{3}{5}\), to be trivial examples.
There are exactly four non-trivial examples of this type of fraction, less than one in value, and containing two digits in the numerator and denominator.
If the product of these four fractions is given in its lowest common terms, find the value of the denominator.
Solution Discussion¶
Nothing special here, just implement the faulty cancelling logic and see when it just so happens to agree with the correct arithmetic results. Accumulate these so-called curious fractions.
Solution Implementation¶
from fractions import Fraction
from typing import Tuple
from lib.digital import digits_of, digits_to_num
def simplify_bad(a: int, b: int) -> Tuple[int, int]:
""" Simplify the fraction :math:`\\frac{a}{b}` using the incorrect logic specified for this problem
In particular, individual decimal digits are directly cancelled with each other rather than considering common
divisors of :math:`a` and :math:`b`.
:param a: fraction numerator
:param b: fraction denominator
:return: the simplified fraction :math:`\\frac{a}{b}` using the incorrect logic
>>> simplify_bad(49, 98)
(4, 8)
"""
# Get the decimal digits of the numerator and denominator
a_digits = digits_of(a, base=10)
b_digits = digits_of(b, base=10)
# Build copies of the digits and start cancel them out if possible
aa = a_digits.copy()
bb = b_digits.copy()
for a_i in a_digits:
if a_i in bb and a_i != 0:
aa.remove(a_i)
bb.remove(a_i)
return digits_to_num(aa), digits_to_num(bb) # convert the remaining digits back to integers
def is_curious_fraction(x: int, y: int, x_bad: int, y_bad: int) -> bool:
""" Test to see if :math:`\\frac{x}{y}` is a "curious fraction"
:param x: the numerator of the fraction
:param y: the denominator of the fraction
:param x_bad: the numerator of the (incorrectly) cancelled fraction
:param y_bad: the denominator of the (incorrectly) cancelled fraction
:return: whether :math:`\\frac{x}{y}` is a "curious fraction" or not
"""
if x_bad == x:
return False # no cancellation occurred, cannot be "curious"
elif y_bad == 0:
return False # cancellation resulted in invalid fraction
else:
return x_bad / y_bad == x / y
def solve():
""" Compute the answer to Project Euler's problem #33 """
n_digits = 2 # number of digits in each numerator/denominator
curious_product = Fraction(1, 1) # will accumulate the product of curious fractions
for numerator in range(10 ** (n_digits - 1), 10 ** n_digits):
for denominator in range(numerator + 1, 10 ** n_digits):
numerator_cancelled, denominator_cancelled = simplify_bad(numerator, denominator)
if is_curious_fraction(numerator, denominator, numerator_cancelled, denominator_cancelled):
curious_product *= Fraction(numerator_cancelled, denominator_cancelled)
answer = curious_product.denominator # extract the denominator from the simplified fraction
return answer
expected_answer = 100
-
solutions.problem33.
is_curious_fraction
(x, y, x_bad, y_bad)¶ Test to see if \(\frac{x}{y}\) is a “curious fraction”
Parameters: - x (
int
) – the numerator of the fraction - y (
int
) – the denominator of the fraction - x_bad (
int
) – the numerator of the (incorrectly) cancelled fraction - y_bad (
int
) – the denominator of the (incorrectly) cancelled fraction
Return type: bool
Returns: whether \(\frac{x}{y}\) is a “curious fraction” or not
- x (
-
solutions.problem33.
simplify_bad
(a, b)¶ Simplify the fraction \(\frac{a}{b}\) using the incorrect logic specified for this problem
In particular, individual decimal digits are directly cancelled with each other rather than considering common divisors of \(a\) and \(b\).
Parameters: - a (
int
) – fraction numerator - b (
int
) – fraction denominator
Return type: Tuple
[int
,int
]Returns: the simplified fraction \(\frac{a}{b}\) using the incorrect logic
>>> simplify_bad(49, 98) (4, 8)
- a (
-
solutions.problem33.
solve
()¶ Compute the answer to Project Euler’s problem #33