def pandigital_products(digits: Iterable[int]) -> Iterable[int]: digits = set(digits) digits_count = len(digits) multipliers_digits_counts_sum = ceil(digits_count / 2) product_multiplier_digits_count = digits_count - multipliers_digits_counts_sum max_left_multipliers_digits_count = ceil((multipliers_digits_counts_sum - 1) / 2) for left_multiplier_digits_count in range(1, max_left_multipliers_digits_count + 1): right_multiplier_digits_count = (multipliers_digits_counts_sum - left_multiplier_digits_count) left_multipliers_digits = permutations(digits, r=left_multiplier_digits_count) for left_multiplier_digits in left_multipliers_digits: allowed_right_multipliers_digits = digits - set(left_multiplier_digits) right_multipliers_digits = permutations(allowed_right_multipliers_digits, r=right_multiplier_digits_count) for right_multiplier_digits in right_multipliers_digits: allowed_products_digits = (allowed_right_multipliers_digits - set(right_multiplier_digits)) products_digits = permutations(allowed_products_digits, r=product_multiplier_digits_count) for product_digits in products_digits: left_multiplier = digits_to_number(left_multiplier_digits) right_multiplier = digits_to_number(right_multiplier_digits) product = digits_to_number(product_digits) if left_multiplier * right_multiplier == product: yield product
def curious_fractions(numbers: Iterable[int]) -> Iterable[Fraction]: fractions_parts = permutations(numbers, r=2) filtered_fractions_parts = star_filter( non_trivial_fraction, star_filter(operator.lt, fractions_parts)) for numerator, denominator in filtered_fractions_parts: numerator_digits = list(number_to_digits(numerator)) denominator_digits = list(number_to_digits(denominator)) try: common_digit, = set(numerator_digits) & set(denominator_digits) except ValueError: continue else: cancelled_numerator_digits = numerator_digits[:] cancelled_numerator_digits.remove(common_digit) cancelled_denominator_digits = denominator_digits[:] cancelled_denominator_digits.remove(common_digit) cancelled_numerator = digits_to_number(cancelled_numerator_digits) cancelled_denominator = digits_to_number( cancelled_denominator_digits) fraction = Fraction(numerator, denominator) cancelled_fraction = Fraction(cancelled_numerator, cancelled_denominator) fraction_is_curious = fraction == cancelled_fraction if fraction_is_curious: yield fraction
def pandigital_multiples(digits: Collection[int]) -> Iterator[int]: position_stop = ceil(len(digits) / 2) for digits in permutations(digits): for position in range(1, position_stop): base_digits = digits[:position] base = digits_to_number(base_digits) base_digits_set = set(base_digits) tail_start = position for multiplier in range(2, 10): tail = ''.join(map(str, digits[tail_start:])) if not tail: continue step = base * multiplier step_digits = list(number_to_digits(step)) step_digits_set = set(step_digits) step_digits_count = len(step_digits) if step_digits_count < len(step_digits_set): break if step_digits_set & base_digits_set: break if not tail.startswith(str(step)): break tail_start += step_digits_count else: yield digits_to_number(digits)
def sub_string_divisible_numbers(*, digits: Iterable[int], slicers_by_divisors: Dict[int, slice] ) -> Iterable[int]: for digits in permutations(digits): number = digits_to_number(digits) digits = list(number_to_digits(number)) for divisor, slicer in slicers_by_divisors.items(): digits_slice = digits[slicer] sliced_number = digits_to_number(digits_slice) if sliced_number % divisor: break else: yield number
def truncatable_primes(digits: Tuple[int] = ()) -> Iterable[int]: candidate_digits_count = len(digits) + 1 for digit in possible_digits: candidate_digits = (digit,) + digits candidate_number = digits_to_number(candidate_digits) if not prime(candidate_number): continue if (candidate_digits_count > 1 and all(prime(digits_to_number(candidate_digits[:position])) for position in range(1, candidate_digits_count))): yield candidate_number yield from truncatable_primes(candidate_digits)
from utils import (prime_numbers, digits_to_number, number_to_digits) pandigits = {digit: set(range(1, digit + 1)) for digit in range(1, 10)} def is_pandigital(number: int) -> bool: digits = list(number_to_digits(number)) digits_set = set(digits) if len(digits) > len(digits_set): return False max_digit = max(digits_set) return not (pandigits[max_digit] ^ digits_set) pandigital_primes = filter( is_pandigital, # 9-digit pandigital prime doesn't exist # since # 1 + 2 + ... + 9 = 45 # => it will be always divided by 3 and 9 prime_numbers(digits_to_number(range(8, 0, -1)), reverse=True)) assert next(pandigital_primes) == 7_652_413
def lychrel(number: int) -> bool: for _ in range(MAX_ITERATIONS_COUNT): number += digits_to_number(reversed(list(number_to_digits(number)))) if is_palindrome(str(number)): return False return True