euler/python/lib_misc.py

233 lines
4.5 KiB
Python

from functools import lru_cache
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)
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)
@lru_cache(maxsize=10000)
def factorial(n):
p = 1
for i in range(1, n + 1):
p *= i
return p
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
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
@lru_cache(maxsize=1000000)
def gcd(a, b):
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))
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
def cache(f):
cache = {}
def func_cached(*args):
if args in cache:
return cache[args]
r = f(*args)
cache[args] = r
return r
return func_cached