solutions.problem33.solve()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nop 0
dl 0
loc 14
rs 9.9
c 0
b 0
f 0
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
Coding Style introduced by
This line is too long as per the coding-style (118/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style introduced by
This line is too long as per the coding-style (118/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style introduced by
This line is too long as per the coding-style (110/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style Naming introduced by
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.

Loading history...
Coding Style Naming introduced by
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.

Loading history...
46
    """ Simplify the fraction :math:`\\frac{a}{b}` using the incorrect logic specified for this problem
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
47
48
    In particular, individual decimal digits are directly cancelled with each other rather than considering common
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style Naming introduced by
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.

Loading history...
65
    bb = b_digits.copy()
0 ignored issues
show
Coding Style Naming introduced by
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.

Loading history...
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
Coding Style Naming introduced by
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.

Loading history...
Coding Style Naming introduced by
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.

Loading history...
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
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
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
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
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
Coding Style Naming introduced by
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.

Loading history...
109