Passed
Push — master ( c77022...c70656 )
by Ken M.
02:20
created

ugly_numbers.Factors.__init__()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
from functools import lru_cache, cmp_to_key
2
from itertools import product, starmap
3
from math import log
4
from operator import mul
5
6
PRIME_FACTORS = (2, 3, 5)
7
8
9
class Factors:
10
    def __init__(self, factor):
11
        self.factor = factor
12
        self.current = 1
13
14
    def pop(self, value: int) -> int | None:
15
        if value == self.current:
16
            ret = self.current
17
            self.current *= self.factor
18
            return ret
19
20
21
@lru_cache
22
def is_ugly(n: int) -> bool:
23
    if n == 1:
24
        return True
25
    for prime in PRIME_FACTORS:
26
        while n % prime == 0:
27
            n //= prime
28
    return n == 1
29
30
31
def find_ugly_number(n: int, ugly_numbers=[1], queue=[1], confirmed_to=1) -> int:
32
    if len(ugly_numbers[:ugly_numbers.index(confirmed_to) + 1]) >= n:
33
        return ugly_numbers[n - 1]
34
    new_numbers = list(starmap(mul, product(PRIME_FACTORS, queue)))
35
    return find_ugly_number(n, sorted(set(ugly_numbers + new_numbers)), new_numbers, min(new_numbers))
36
37
38
def find_ugly_number2(n: int, current_number=1) -> int:
39
    counter = 0
40
    while counter < n:
41
        if is_ugly(current_number):
42
            counter += 1
43
            current_number += 1
44
        else:
45
            current_number += 1
46
    return current_number - 1
47
48
49
def find_ugly_number3(n: int) -> int:
50
    permutations = product(range(n), set([0] + list(range(n * 2 // 3))), set([0] + list(range(n * 2 // 5))))
51
    numbers = sorted([2 ** i[0] * 3 ** i[1] * 5 ** i[2] for i in permutations])
52
    return numbers
53
54
55
def find_ugly_number4(n: int) -> int:
56
    factor_list = [Factors(i) for i in [2, 3, 5, 6, 10, 15, 30]]
57
    for i in range(n):
58
        value_to_pick = min([i.current for i in factor_list])
59
        [i.pop(value_to_pick) for i in factor_list]
60
        print(value_to_pick)
61
62
63
def compare_tuples(a, b):
64
    return (a[0] - b[0]) * log(2) + (a[1] - b[1]) * log(3) + (a[2] - b[2]) * log(5)
65
66
67
def find_ugly_number5(n: int) -> int:
68
    known_ugly_numbers = []
69
    candidates = [(0, 0, 0)]
70
    for _ in range(n):
71
        next_to_add = candidates[0]
72
        del candidates[0]
73
        known_ugly_numbers.append(next_to_add)
74
        new_candidate2 = (next_to_add[0] + 1, next_to_add[1], next_to_add[2])
75
        new_candidate3 = (next_to_add[0], next_to_add[1] + 1, next_to_add[2])
76
        new_candidate5 = (next_to_add[0], next_to_add[1], next_to_add[2] + 1)
77
        for i in [new_candidate2, new_candidate3, new_candidate5]:
78
            if i not in candidates and i not in known_ugly_numbers:
79
                candidates.append(i)
80
        candidates = sorted(candidates, key=cmp_to_key(compare_tuples))
81
    return 2 ** known_ugly_numbers[-1][0] * 3 ** known_ugly_numbers[-1][1] * 5 ** known_ugly_numbers[-1][2]
82
83
84
def ugly_number(n: int) -> int:
85
    return find_ugly_number5(n)
86
87
88
if __name__ == "__main__":
89
    print("Example:")
90
    print(ugly_number(4))
91
92
    # These "asserts" are used for self-checking and not for an auto-testing
93
    assert ugly_number(4) == 4
94
    assert ugly_number(6) == 6
95
    assert ugly_number(11) == 15
96
    print("Ugly Numbers coding complete? Click 'Check' to earn cool rewards!")
97