2019-07-15 05:58:22 +02:00
|
|
|
from functools import lru_cache
|
|
|
|
|
|
|
|
|
2019-07-14 05:58:38 +02:00
|
|
|
def get_digits_reversed(n):
|
|
|
|
"""
|
|
|
|
Returns a list of digits for n.
|
|
|
|
"""
|
|
|
|
digits, rest = [], n
|
|
|
|
while rest != 0:
|
|
|
|
digits.append(rest % 10)
|
|
|
|
rest //= 10
|
|
|
|
return digits
|
|
|
|
|
|
|
|
|
|
|
|
def is_palindrome_string(n):
|
|
|
|
"""
|
|
|
|
Checks whether n is a palindrome number
|
|
|
|
with a string based algorithm.
|
|
|
|
|
|
|
|
:param n: number to check
|
|
|
|
:returns: boolean
|
|
|
|
|
|
|
|
"""
|
|
|
|
s = str(n)
|
|
|
|
return s == s[::-1]
|
|
|
|
|
|
|
|
|
|
|
|
def is_palindrome_integer(n):
|
|
|
|
"""
|
|
|
|
Checks whether n is a palindrome number
|
|
|
|
with an integer based algorithm.
|
|
|
|
|
|
|
|
:param n: number to check
|
|
|
|
:returns: boolean
|
|
|
|
"""
|
|
|
|
digits = get_digits_reversed(n)
|
|
|
|
digits_count = len(digits)
|
|
|
|
for i in range(digits_count // 2):
|
|
|
|
if digits[i] != digits[digits_count - i - 1]:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def time_is_palindrom():
|
|
|
|
""" I just want to check which option is faster. """
|
|
|
|
from timeit import timeit
|
|
|
|
print(timeit(lambda: is_palindrome_integer(82445254428), number=100000))
|
|
|
|
print(timeit(lambda: is_palindrome_string(82445254428), number=100000))
|
|
|
|
# > 0.42330633100027626
|
|
|
|
# > 0.07605635199979588
|
|
|
|
|
|
|
|
|
|
|
|
is_palindrome = is_palindrome_string
|
|
|
|
|
|
|
|
|
|
|
|
def get_item_counts(l):
|
|
|
|
"""
|
|
|
|
Counts how often each element is part of the list.
|
|
|
|
|
|
|
|
:param l: a list
|
|
|
|
:returns: a dictionary
|
|
|
|
"""
|
|
|
|
d = {}
|
|
|
|
for e in l:
|
|
|
|
try:
|
|
|
|
d[e] += 1
|
|
|
|
except KeyError:
|
|
|
|
d[e] = 1
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
|
|
|
def product(l):
|
|
|
|
"""
|
|
|
|
Calculates the product of all items in the list.
|
|
|
|
|
|
|
|
:param l: a list
|
|
|
|
:returns: a number that is the product of all elements in the list
|
|
|
|
"""
|
|
|
|
from functools import reduce
|
|
|
|
import operator
|
|
|
|
return reduce(operator.mul, l, 1)
|
2019-07-15 05:58:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
def triangle_numbers():
|
|
|
|
c = 0
|
|
|
|
i = 1
|
|
|
|
while True:
|
|
|
|
c += i
|
|
|
|
yield c
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
|
|
def even(n):
|
|
|
|
"""
|
|
|
|
Returns true if a number is even.
|
|
|
|
"""
|
|
|
|
return n % 2 == 0
|
|
|
|
|
|
|
|
|
|
|
|
def odd(n):
|
|
|
|
"""
|
|
|
|
Returns true if a number is odd.
|
|
|
|
"""
|
|
|
|
return n % 2 != 0
|
|
|
|
|
|
|
|
|
|
|
|
def collatz_sequence(n):
|
|
|
|
"""
|
|
|
|
Returns collatz sequence for n.
|
|
|
|
|
|
|
|
:param n: collatz sequence
|
|
|
|
"""
|
|
|
|
cs = []
|
|
|
|
while n != 1:
|
|
|
|
cs.append(n)
|
|
|
|
n = n // 2 if n % 2 == 0 else 3 * n + 1
|
|
|
|
cs.append(n)
|
|
|
|
return cs
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache(maxsize=1000000)
|
|
|
|
def collatz_sequence_length(n):
|
|
|
|
"""
|
|
|
|
Returns length of collatz sequence for n.
|
|
|
|
|
|
|
|
:param n: collatz sequence
|
|
|
|
"""
|
|
|
|
if n == 1:
|
|
|
|
return 1
|
|
|
|
length = 1
|
|
|
|
while odd(n):
|
|
|
|
n = 3 * n + 1
|
|
|
|
length += 1
|
|
|
|
return length + collatz_sequence_length(n // 2)
|
|
|
|
|
|
|
|
|
2019-07-16 04:11:49 +02:00
|
|
|
@lru_cache(maxsize=10000)
|
|
|
|
def factorial(n):
|
2019-07-16 18:51:07 +02:00
|
|
|
p = 1
|
|
|
|
for i in range(1, n + 1):
|
|
|
|
p *= i
|
|
|
|
return p
|
2019-07-16 04:11:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
def proper_divisors(n):
|
|
|
|
"""
|
|
|
|
Returns the list of divisors for n excluding n.
|
|
|
|
"""
|
|
|
|
if n < 2:
|
|
|
|
return []
|
|
|
|
divisors = [1, ]
|
|
|
|
d = 2
|
|
|
|
while d * d <= n:
|
|
|
|
if n % d == 0:
|
|
|
|
divisors.append(d)
|
|
|
|
d += 1
|
|
|
|
# Ignore first element and iterate list backwards.
|
|
|
|
for d in divisors[1:][::-1]:
|
|
|
|
q = n // d
|
|
|
|
if q != d:
|
|
|
|
divisors.append(q)
|
|
|
|
return divisors
|
|
|
|
|
|
|
|
|
|
|
|
def sum_proper_divisors(n):
|
|
|
|
"""
|
|
|
|
Returns the sum of proper divisors of a number.
|
|
|
|
"""
|
|
|
|
if n < 2:
|
|
|
|
return 0
|
|
|
|
s = 1
|
|
|
|
d = 2
|
|
|
|
while d * d <= n:
|
|
|
|
if n % d == 0:
|
|
|
|
s += d
|
|
|
|
q = n // d
|
|
|
|
if q != d:
|
|
|
|
s += q
|
|
|
|
d += 1
|
|
|
|
return s
|
2019-07-16 18:51:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
def permutations(iterable):
|
|
|
|
"""
|
|
|
|
Generator that returns all permutations for the iterable.
|
|
|
|
|
|
|
|
Generates equivalent result to itertools.permutations.
|
|
|
|
"""
|
|
|
|
if not iterable:
|
|
|
|
yield iterable
|
|
|
|
for i in range(len(iterable)):
|
|
|
|
elem = iterable[i:i + 1]
|
|
|
|
rest = iterable[:i] + iterable[i + 1:]
|
|
|
|
for ps in permutations(rest):
|
|
|
|
yield elem + ps
|
|
|
|
|
|
|
|
|
2019-08-01 15:38:51 +02:00
|
|
|
@lru_cache(maxsize=1000000)
|
2019-07-16 18:51:07 +02:00
|
|
|
def gcd(a, b):
|
2019-07-18 20:23:44 +02:00
|
|
|
while a % b != 0:
|
|
|
|
a, b = b, a % b
|
|
|
|
return b
|
|
|
|
|
|
|
|
|
|
|
|
def get_digit_count(n):
|
|
|
|
"""
|
|
|
|
Returns the number of digits for n.
|
|
|
|
"""
|
|
|
|
return len(str(n))
|
2019-07-21 20:13:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
def is_permutation(n, p):
|
|
|
|
""" Checks if p is a permutation of n. """
|
|
|
|
digit_counts_n = [0 for _ in range(10)]
|
|
|
|
digit_counts_p = [0 for _ in range(10)]
|
|
|
|
for d_n in str(n):
|
|
|
|
digit_counts_n[int(d_n)] += 1
|
|
|
|
for p_n in str(p):
|
|
|
|
digit_counts_p[int(p_n)] += 1
|
|
|
|
return digit_counts_n == digit_counts_p
|