-1

I found this problem in a cp contest which is over now so it can be answered. Three primes (p1,p2,p3) (not necessarily distinct) are called special if (p1+p2+p3) divides p1*p2*p3. We have to find the number of these special pairs if the primes can't exceed 10^6

I tried brute force method but it timed out. Can there be any other method?

I_Love_Islam
  • 73
  • 1
  • 3
  • 10
  • I'm voting to close this question as off-topic because it belongs on [maths](https://math.stackexchange.com/) – Brett Hale Jul 08 '19 at 20:47

2 Answers2

1

If you are timing out, then you need to do some smart searching to replace brute force. There are just short of 80,000 primes below a million so it is not surprising you timed out.

So, you need to start looking more carefully.

For example, any triple (2, p, p+2) where p+2 is also prime will meet the criteria:

  • 2 + 3 + 5 = 10; 2 * 3 * 5 = 30; 30 / 10 = 3.
  • 2 + 5 + 7 = 14; 2 * 5 * 7 = 70. 70 / 14 = 5.
  • ...
  • 2 + p + p+2 = 2(p+2); 2 * p * (p+2) = 2p(p+2); 2p(p+2) / 2(p+2) = p.
  • ...

Are there other triples that start with 2? Are there triples that start with 3? What forms do p2 and p3 take if p1= 3? Run your program for triples up to 500 or so and look for patterns in the results. Then extrapolate those results to 10^6.

I assume you are using a Sieve to generate your initial list of primes.

rossum
  • 15,344
  • 1
  • 24
  • 38
1

I've experimented with this problem since you posted it. I've not solved it, but wanted to pass along what insight I have before I move onto something else:

Generating Primes is Not the Issue

With a proper sieve algorithm, we can generate all primes under 10**6 in a fraction of a second. (Less than 1/3 of a second on my Mac mini.) Spending time optimizing prime generation beyond this is time wasted.

The Brute Force Method

If we try to generate all permutations of three primes in Python, e.g.:

for prime_1 in primes:
    for prime_2 in primes:
        if prime_2 < prime_1:
            continue
        for prime_3 in primes:
            if prime_3 < prime_2:
                continue
            pass

Or better yet, push the problem down to the C level via Python's itertools:

from itertools import combinations_with_replacement

for prime_1, prime_2, prime_3 in combinations_with_replacement(primes, 3):
    pass

Then, our timings, doing no actual work except generating prime triples, looks like:

         sec.
10**2    0.04
10**3    0.13
10**4   37.37
10**5     ?

You can see how much time increases with each order of magnitude. Here's my example of a brute force solution:

from itertools import combinations_with_replacement

def sieve_primes(n):  # assumes n > 1
    sieve = [False, False, True] + [True, False] * ((n - 1) // 2)
    p = 3

    while p * p <= n:
        if sieve[p]:
            for i in range(p * p, n + 1, p):
                sieve[i] = False
        p += 2

    return [number for number, isPrime in enumerate(sieve) if isPrime]

primes = sieve_primes(10 ** 3)
print("Finished Sieve:", len(primes), "primes")

special = 0

for prime_1, prime_2, prime_3 in combinations_with_replacement(primes, 3):
    if (prime_1 * prime_2 * prime_3) % (prime_1 + prime_2 + prime_3) == 0:
        special += 1

print(special)

Avoid Generating Triples, but Still Brute Force

Here's an approach that avoids generating triples. We take the smallest and largest primes we generated, cube them, and loop over them with a custom factoring function. This custom factoring function only returns a value for those numbers that are made up of exactly three prime factors. For any number made up of more or less, it returns None. This should be faster than normal factoring as the function can give up early.

Numbers that factor into exactly three primes are easy to test for specialness. We're going to pretend our custom factoring function takes no time at all and simply measure how long it takes us to loop over all the numbers in question:

smallest_factor, largest_factor = primes[0], primes[-1]

for number in range(smallest_factor**3, largest_factor**3):
    pass

Again, some timings:

           sec.
10**2      0.14
10**3    122.39
10**4       ?

Doesn't look promising. In fact, worse than our original brute force method. And our custom factoring function in reality adds a lot of time. Here's my example of this solution (copy sieve_primes() from the previous example):

def factor_number(n, count):
    size = 0
    factors = []

    for prime in primes:
        while size < count and n % prime == 0:
            factors.append(prime)
            n //= prime
            size += 1

        if n == 1 or size == count:
            break

    if n > 1 or size < count:
        return None

    return factors

primes = sieve_primes(10 ** 3)
print("Finished Sieve:", len(primes), "primes")

special = 0

smallest_factor, largest_factor = primes[0], primes[-1]

for number in range(smallest_factor**3, largest_factor**3):
    factors = factor_number(number, 3)

    if factors:
        if number % sum(factors) == 0:
            special += 1

print(special)
cdlane
  • 40,441
  • 5
  • 32
  • 81