Пример #1
0
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]}")
Пример #2
0
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]}")
Пример #3
0
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 :-(")
Пример #4
0
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
Пример #5
0
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}")
Пример #6
0
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}")
Пример #7
0
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})"
    )