def main(): """ Entry point """ # (After reading the pdf for problem 69...!) we know n/phi(n) = prod(p / (p-1)) # for p in all the primes dividing n. # Therefore n/phi(n) is at a minimum when prod(p / (p-1)) is at a minimum # which would happen when we have a small number of large prime factors. # Because phi(prime) = prime - 1, we can't use just one prime as phi(p) won't be a permutation of p # We should check composites of a couple of primes. Up to 10^5 should do # because we want two primes as high as possible print("Getting primes below 100000...") primes = get_primes(100000) # Now search combinations of two primes print("Searching products of two primes for phi(p) == permutation(p)...") best = None for index, p1 in enumerate(primes): for p2 in primes[index+1:]: n = p1 * p2 if n > 10 ** 7: break phi = n * (1 - Fraction(1, p1)) * (1 - Fraction(1, p2)) if is_permutation(n, phi): print(f"Hello: {n} = {p1} * {p2}") if not best or (best[2] >= n/phi): best = (n, phi, n/phi) print(f"Best: n={best[0]}, with phi(n)={best[1]}")
def main(): """ Entry point """ # Get all primes up to 10000 primes = get_primes(10000) prime_set = set(primes) triples = [] for first_prime_index, first_prime in enumerate(primes): if first_prime < 1000: continue # OK, let's see if this and any other prime will form an arithmetic seq to get a third for second_prime_index in range(first_prime_index + 1, len(primes)): second_prime = primes[second_prime_index] third_prime_candidate = 2 * second_prime - first_prime if third_prime_candidate in prime_set: # Are the numbers permutations of one another? p1_digits = get_digits(first_prime) p2_digits = get_digits(second_prime) p3_digits = get_digits(third_prime_candidate) if sorted(p1_digits) == sorted(p2_digits) == sorted(p3_digits): triples.append( (first_prime, second_prime, third_prime_candidate)) print(f"Triple found! {triples[-1]}")
def main(): """ Entry point """ # Get a bunch of primes to test. Guess how many digits we might need to search up to. max_num_digits = 6 family_size = 8 primes = get_primes(10**max_num_digits) # We know the first prime with 8 family members will be > 56003, try 5 digits first for n_digits in range(5, max_num_digits + 1): print(f"Trying {n_digits} digits...") # Get the set of candidates for this digit count n_digit_primes = [p for p in primes if num_digits(p) == n_digits] n_digit_prime_set = set(n_digit_primes) for base_prime in n_digit_primes: # We want at least 8 primes, so we need to have the last digit as 1, 3, 5, or 7 # (i.e. the last digit of this prime can't be a replacement candidate) for num_replacement_digits in range(1, n_digits): digit_pattern = [1] * num_replacement_digits + [0] * ( n_digits - 1 - num_replacement_digits) for replacement_pattern in set( itertools.permutations(digit_pattern, n_digits - 1)): # We need family_size primes, so as soon as we have (10 - family_size + 1) non-primes, we're "bust" num_non_primes = 0 for replacement_digit in range(10): # Make the replacement (counting digits from right to left) candidate = 0 for digit in range(n_digits): digit_multiplier = 10**digit if digit == 0 or not replacement_pattern[-digit]: # Use the original digit candidate += ( (base_prime // digit_multiplier) % 10) * digit_multiplier else: # Use the replacement digit candidate += replacement_digit * digit_multiplier # Test it if candidate not in n_digit_prime_set: num_non_primes += 1 if num_non_primes > 10 - family_size: # Next replacement pattern break if num_non_primes <= (10 - family_size): # We found it! print( f"Found prime: {base_prime}, replacement pattern: {replacement_pattern}" ) return print("Found nothing :-(")
def count_prime_ways(n, primes=None, cache=None): """ Count the number of ways that n can be made from sum of at least 1 prime (or at least 2 primes if primes is initially None) primes: List of all primes we're allowed to use, from 2 upwards """ # Get the prime lookup table once only if not primes: primes = get_primes(n) # Create memoizing cache with keys of (n, largest prime) if not cache: cache = {(2, 2): 1} cached_result = cache.get((n, primes[-1]), None) if cached_result: return cached_result # Special case if this is the last prime: can we get n from summing it i.e. is it a factor? if len(primes) == 1: # Well, we know the prime is 2. Is n even? ways = 1 if n % 2 == 0 else 0 cache[(n, 2)] = ways return ways # Use the largest prime remaining largest_prime = primes[-1] remainder = n ways = 0 while remainder > largest_prime: remainder -= largest_prime # No joy if remainder is 1 - can't make this from primes if remainder == 1: break # We can't use primes larger than remainder reverse_index = -2 while primes[reverse_index] > remainder: reverse_index -= 1 ways += count_prime_ways(remainder, primes[:reverse_index + 1], cache) if remainder == largest_prime: ways += 1 # Now count all the ways with a smaller prime if len(primes) > 1: ways += count_prime_ways(n, primes[:-1], cache) cache[(n, largest_prime)] = ways return ways
def main(): """ Entry point """ # We have n/d where n<d. For reduced proper fractions, n and d must be coprime. # So for denominator d, there are phi(d) numerators, and we want sum(phi(d)), 1 < d <= 10^6 print("Getting primes up to 1000000...") primes = get_primes(1000000) print("Summing phi(n)...") phi_sum = 0 for n in range(2, 1000001): if n % 10000 == 0: print(f"{n}") phi_sum += totient(n, primes) print(f"sum(phi(n)): {phi_sum}")
def main(): """ Entry point """ print("Get primes below one million...") primes = get_primes(1000000) prime_set = set(primes) # Now get the n-consecutive sums for each n-length starting from 6 longest = None for n in range(6, len(primes)): # Initialize with sum of first n print(f"Try {n} consecutive primes...") consecutive_sums = [sum(primes[:n])] found_longest = False for i in range(1, len(primes) - n): # Can quickly compute the next one by adding the next prime and removing the previous first next_sum = consecutive_sums[-1] + primes[i + n - 1] - primes[i - 1] if next_sum > primes[-1]: # No point going any further for sequences of this length break if next_sum in prime_set: # We have a winner (for this length). On to the next length! print(f"Length = {n}, first = {primes[i]}, sum = {next_sum}") longest = next_sum found_longest = True break consecutive_sums.append(next_sum) if not found_longest and i == 1: # We grew too big on the first try. No more sequences to try break print(f"Longest: {longest}")
def main(): """ Entry point """ pair_set_size = 5 # Grab a load of primes to work with max_digits_in_prime_cache = 4 primes = get_primes(10**max_digits_in_prime_cache) prime_set = set(primes) # See which primes concatenate both ways to form new primes # We want the lowest sum of pair_set_size primes, so start with the smallest # and work our way up testing against all smaller primes prime_pairs = defaultdict(set) winning_sets = [] for new_smallest_prime in primes: new_prime_pairs = set() for other_prime in prime_pairs.keys(): if not is_prime_when_concatenated(new_smallest_prime, other_prime, prime_set, primes[-1]): continue # Yes, we have a valid pair new_prime_pairs.add(other_prime) prime_pairs[other_prime].add(new_smallest_prime) # Are the sets big enough? if len(new_prime_pairs) >= pair_set_size - 1: # Could be - check all pair combos that include our new pair possible_candidates = new_prime_pairs.union( {new_smallest_prime}) for candidate_set in combinations(possible_candidates, pair_set_size): # See if all pairs work for this combo is_winning_set = True for pairwise_combo in combinations(candidate_set, 2): if new_smallest_prime in set(pairwise_combo): # We know this pair is fine already continue elif pairwise_combo[1] not in prime_pairs[ pairwise_combo[0]]: # This combination doesn't work is_winning_set = False break if is_winning_set: winning_sets.append(candidate_set) if winning_sets: break if winning_sets: break prime_pairs[new_smallest_prime] = new_prime_pairs # Get the best (smallest sum) winning set final_pair_set = min(winning_sets, key=lambda s: sum(s)) print( f"Sum of {pair_set_size} primes: {sum(final_pair_set)} (primes: {final_pair_set})" )