1 | """ |
||
2 | Project Euler Problem 33: Digit Cancelling Fractions |
||
3 | ==================================================== |
||
4 | |||
5 | .. module:: solutions.problem33 |
||
6 | :synopsis: My solution to problem #33. |
||
7 | |||
8 | The source code for this problem can be |
||
9 | `found here <https://bitbucket.org/nekedome/project-euler/src/master/solutions/problem33.py>`_. |
||
10 | |||
11 | Problem Statement |
||
12 | ################# |
||
13 | |||
14 | The fraction :math:`\\frac{49}{98}` is a curious fraction, as an inexperienced mathematician in attempting to simplify |
||
0 ignored issues
–
show
|
|||
15 | it may incorrectly believe that :math:`\\frac{49}{98} = \\frac{4}{8}`, which is correct, is obtained by cancelling the |
||
0 ignored issues
–
show
|
|||
16 | :math:`9\\mbox{s}`. |
||
17 | |||
18 | We shall consider fractions like, :math:`\\frac{30}{50} = \\frac{3}{5}`, to be trivial examples. |
||
19 | |||
20 | There are exactly four non-trivial examples of this type of fraction, less than one in value, and containing two digits |
||
0 ignored issues
–
show
|
|||
21 | in the numerator and denominator. |
||
22 | |||
23 | If the product of these four fractions is given in its lowest common terms, find the value of the denominator. |
||
0 ignored issues
–
show
|
|||
24 | |||
25 | Solution Discussion |
||
26 | ################### |
||
27 | |||
28 | Nothing special here, just implement the faulty cancelling logic and see when it just so happens to agree with the |
||
0 ignored issues
–
show
|
|||
29 | correct arithmetic results. Accumulate these so-called curious fractions. |
||
30 | |||
31 | Solution Implementation |
||
32 | ####################### |
||
33 | |||
34 | .. literalinclude:: ../../solutions/problem33.py |
||
35 | :language: python |
||
36 | :lines: 39- |
||
37 | """ |
||
38 | |||
39 | from fractions import Fraction |
||
40 | from typing import Tuple |
||
41 | |||
42 | from lib.digital import digits_of, digits_to_num |
||
43 | |||
44 | |||
45 | def simplify_bad(a: int, b: int) -> Tuple[int, int]: |
||
0 ignored issues
–
show
The name
a does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() The name
b does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
46 | """ Simplify the fraction :math:`\\frac{a}{b}` using the incorrect logic specified for this problem |
||
0 ignored issues
–
show
|
|||
47 | |||
48 | In particular, individual decimal digits are directly cancelled with each other rather than considering common |
||
0 ignored issues
–
show
|
|||
49 | divisors of :math:`a` and :math:`b`. |
||
50 | |||
51 | :param a: fraction numerator |
||
52 | :param b: fraction denominator |
||
53 | :return: the simplified fraction :math:`\\frac{a}{b}` using the incorrect logic |
||
54 | |||
55 | >>> simplify_bad(49, 98) |
||
56 | (4, 8) |
||
57 | """ |
||
58 | |||
59 | # Get the decimal digits of the numerator and denominator |
||
60 | a_digits = digits_of(a, base=10) |
||
61 | b_digits = digits_of(b, base=10) |
||
62 | |||
63 | # Build copies of the digits and start cancel them out if possible |
||
64 | aa = a_digits.copy() |
||
0 ignored issues
–
show
The name
aa does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
65 | bb = b_digits.copy() |
||
0 ignored issues
–
show
The name
bb does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
66 | for a_i in a_digits: |
||
67 | if a_i in bb and a_i != 0: |
||
68 | aa.remove(a_i) |
||
69 | bb.remove(a_i) |
||
70 | |||
71 | return digits_to_num(aa), digits_to_num(bb) # convert the remaining digits back to integers |
||
72 | |||
73 | |||
74 | def is_curious_fraction(x: int, y: int, x_bad: int, y_bad: int) -> bool: |
||
0 ignored issues
–
show
The name
x does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() The name
y does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
75 | """ Test to see if :math:`\\frac{x}{y}` is a "curious fraction" |
||
76 | |||
77 | :param x: the numerator of the fraction |
||
78 | :param y: the denominator of the fraction |
||
79 | :param x_bad: the numerator of the (incorrectly) cancelled fraction |
||
80 | :param y_bad: the denominator of the (incorrectly) cancelled fraction |
||
81 | :return: whether :math:`\\frac{x}{y}` is a "curious fraction" or not |
||
82 | """ |
||
83 | |||
84 | if x_bad == x: |
||
0 ignored issues
–
show
|
|||
85 | return False # no cancellation occurred, cannot be "curious" |
||
86 | elif y_bad == 0: |
||
87 | return False # cancellation resulted in invalid fraction |
||
88 | else: |
||
89 | return x_bad / y_bad == x / y |
||
90 | |||
91 | |||
92 | def solve(): |
||
93 | """ Compute the answer to Project Euler's problem #33 """ |
||
94 | |||
95 | n_digits = 2 # number of digits in each numerator/denominator |
||
96 | curious_product = Fraction(1, 1) # will accumulate the product of curious fractions |
||
97 | |||
98 | for numerator in range(10 ** (n_digits - 1), 10 ** n_digits): |
||
99 | for denominator in range(numerator + 1, 10 ** n_digits): |
||
100 | numerator_cancelled, denominator_cancelled = simplify_bad(numerator, denominator) |
||
101 | if is_curious_fraction(numerator, denominator, numerator_cancelled, denominator_cancelled): |
||
0 ignored issues
–
show
|
|||
102 | curious_product *= Fraction(numerator_cancelled, denominator_cancelled) |
||
103 | |||
104 | answer = curious_product.denominator # extract the denominator from the simplified fraction |
||
105 | return answer |
||
106 | |||
107 | |||
108 | expected_answer = 100 |
||
0 ignored issues
–
show
The name
expected_answer does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
109 |
This check looks for lines that are too long. You can specify the maximum line length.