4

Given a number N, how do we find maximum P*Q < N, such that P and Q are prime numbers?

My (brute force) attempt:

  • find a list {P, N/P} for all primes P < √N
  • find a list of primes Q, such that Q is the largest prime just below N/P in the list above
  • Determine the maximum product P*Q from above

While this brute force approach will work, is there a formal (more sensible) solution to this question?

Example: N=27

√N = 5.196

Candidate primes: 2,3,5 --> [{2,13.5},{3,9},{5,5.4}] ->[{2,13},{3,7},{5,5}]

Solution: Max([2*13, 3*7, 5*5]) = 2*13 = 26

Hence, the brute force solution works.

Taking this one step further, we see that Q_max <= N/2 and if indeed we agree that P < Q, then we have Q >= √N.

We can refine our solution set to only those values {P, N\2}, where N\2 >= √N.

I have opted for integer division "\", since we are only interested in integer values, and integer division is indeed much faster than regular division "/"

The problem reduces to:

Example: N=27

√N = 5.196

Candidate P: 2,3 --> [{2,13},{3,9}] -->[{2,13},{3,7}]

(we drop {5,5} since N\P < √N i.e. 5 < 5.196)

Solution set: max([2*13, 3*7]) = 2*13 = 26

It might look trivial, but it just eliminated 1/3 of the possible solution set.

Are there other clever procedures we can add to reduce the set further?

Charles Okwuagwu
  • 10,538
  • 16
  • 87
  • 157
  • You might get more answers if you ask this on [Programmers.SE](http://programmers.stackexchange.com/) – Hexaholic Aug 25 '15 at 09:21
  • 2
    you have mentioned co-prime in question but you have asked 'P and Q are prime number?'. You need primes or co-primes? – SureshS Aug 25 '15 at 09:21
  • @SureshS, ah i need just primes please, thanks for spotting that – Charles Okwuagwu Aug 25 '15 at 09:22
  • @Hexaholic when referring other sites, it is often helpful to point that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info) – gnat Aug 25 '15 at 09:23
  • @SureshS, but wait i am still correct :) i asked for maximum co prime --> product of two primes which are less than given N – Charles Okwuagwu Aug 25 '15 at 09:24
  • 3
    This is a known hard problem. If you solve it efficiently, you break RSA encryption and became rich and/or famous. – n. m. could be an AI Aug 25 '15 at 09:29
  • @n.m. You might want to write an answer for this; I'd be happy to see the details. – anatolyg Aug 25 '15 at 09:34
  • @anatolyg: see this question for some explanation: http://stackoverflow.com/questions/439870/why-are-primes-important-in-cryptography – Ralph M. Rickenbach Aug 25 '15 at 09:37
  • 1
    any two (non-equal) primes are always coprime with each other, that's redundant. so, do you forbid P*Q where P==Q? if not, then "coprime" in the title must go. – Will Ness Aug 25 '15 at 09:40
  • @n.m. Good point (although OP didn't ask for the *factors* so much as the product -- though I think a request for the factors was implicit). But it is clearly feasible though non-trivial for N below a certain level and is sufficiently different from a straight-forward factoring question that it is still an interesting question. – John Coleman Aug 25 '15 at 09:42
  • @n.m. really? I had no clue. It just seems like there would be an obvious solution to this – Charles Okwuagwu Aug 25 '15 at 09:59
  • @WillNess Please how would you reformat the question? The aim is simply to find 2 prime numbers P,Q whose product is max, but less than a given N – Charles Okwuagwu Aug 25 '15 at 10:03
  • @CharlesO Just drop the phrase "Coprime" entirely from your question. "coprime" is a synonym for "relatively prime" -- having no prime factor in common. If you take that literally then P must be different from Q – John Coleman Aug 25 '15 at 10:07
  • 1
    @JohnColeman that P and Q be the same may well be allowed. And without the word "coprime" it _is_ allowed. – Will Ness Aug 25 '15 at 10:12
  • @WillNess by definition co-prime means any product P*Q, where P<> Q right? and as such it my original question was indeed well formatted? asking for both product of 2 primes and co-primes...wouldn't that be a repetition of the same thing? – Charles Okwuagwu Aug 25 '15 at 10:16
  • "P coprime with Q" implies P is not equal to Q, that's all. Any two primes are coprime with each other otherwise, i.e. have GCD == 1: GCD(P,P)==P; GCD(P,Q)==1, where P,Q are prime. – Will Ness Aug 25 '15 at 10:18
  • My brute force solution Is indeed correct. Tested with 27 – Charles Okwuagwu Aug 25 '15 at 10:39
  • @n.m. my brute force solution works. One might just a need a sieve to quickly eliminate obvious non-candidates, P, and a halting algorithm to tell us when we can stop checking other candidates – Charles Okwuagwu Aug 25 '15 at 10:55
  • For small numbers, sure (64 bits is small). – n. m. could be an AI Aug 25 '15 at 11:00
  • You mean it works for N<= UInt64 max. Then by extension it works for all Integers. – Charles Okwuagwu Aug 25 '15 at 11:02
  • @CharlesO the point was that it'll work slowly, because your search must be exhaustive (as the 2- example shows). with state of the art factorization it might be faster to factorize N-1, N-2, ... until you hit the P*Q case. – Will Ness Aug 25 '15 at 11:06
  • @WillNess same difference - how will you know that you have hit Maximum P*Q with your factorization method without an exhaustive search? I still think a means of eliminating non-candidates from [{P,N/P}] would be invaluable. – Charles Okwuagwu Aug 25 '15 at 11:11
  • 2
    @CharlesO because this method factorizes the numbers in descending order. so the first that has just two prime factors is the biggest. :) – Will Ness Aug 25 '15 at 11:12

2 Answers2

2

Another brute force attempt:

  1. Find all prime numbers p <= N/2
  2. Iterate over the array from the smallest p as long as p < √N and multiply it with the largest q < N/p, retaining the largest product.

If enough memory is available (N/2 bit), one could make a bitarray of that size. Initialize it with all TRUE but the first position. Iterating over the bit array, calculate the multiples of the position you are at and set all multiples to false. If the next position is false already, you do not need to recalculate all its multiples, they are already set to false.

Finding all primes therefore is < O(N^2).

a[1] := false;
m := n \ 2; // sizeof(a)
for i := 2 to m do
   a[i] := true;
for i := 2 to m do
   if a[i] then
     for j := 2*a[i] to m step a[i] do
       a[i*j] := false;

Step 2) is < O(n^2) as well:

result := 0;
for i := 2 to √N do
  if not a[i] then continue; // next i;
  for j := (n \ i) downto i do
     if not a[j] then continue; // next j
     if a[j] * a[i] < N
        result :=  max(result, a[j] * a[i]);
        break; // next i;
  if result = N then break; // you are finished

This can be optimized further, I guess. You can keep (i,j) to know the two prime numbers.

Ralph M. Rickenbach
  • 12,893
  • 5
  • 29
  • 49
  • Why the choice of P – Charles Okwuagwu Aug 25 '15 at 10:37
  • Corrected it to p <= N/2. Because 2 is the smallest prime. Any p > N/2 could not be multiplied with another prime number and still be smaller than N. – Ralph M. Rickenbach Aug 25 '15 at 11:03
  • Better choice is P<= √N – Charles Okwuagwu Aug 25 '15 at 11:05
  • 1
    Not for this algorithm. If I take p <= √N, then I have to find q = max{q is prim and q > N/p} to find the largest q*p <= N for each p. That will sum up to find all prime numbers in (√N, N/2]. (Since p >= 2, q will always be <= N/2.) The thing here is that I do not have to find out whether a number is prime, but I generate all prime numbers in one go and keep them in memory, that makes your second step much easier. – Ralph M. Rickenbach Aug 25 '15 at 11:17
  • +1 Nice explanation, although you can make the iteration over the primes smarter and get O(n) performance there, see my answer. – Jaime Aug 26 '15 at 14:02
2

This is similar to what @RalphMRickenback describes, but with tighter complexity bounds.

The prime finding algorithm he describes is the sieve of Erathostenes, which needs space O(n), but has time complexity O(n log log n), you may want to see the discussion on Wikipedia if you want to be more careful about this.

After finding a list of primes smaller than n // 2, you can scan it a single time, i.e. with O(n) complexity, by having a pointer start at the beginning and another at the end. If the product of those two primes is larger than your value, reduce the high pointer. If the product is smaller, compare it to a stored maximum product, and increase the low pointer.

EDIT As mentioned in the comments, the time complexity of the scan of the primes is better than linear on n, since it is only over the primes less than n, so O(n / log n).

Rather than pseudo-code, here's full implementation in Python:

def prime_sieve(n):
    sieve = [False, False] + [True] * (n - 1)

    for num, is_prime in enumerate(sieve):
        if num * num > n:
            break
        if not is_prime:
            continue
        for not_a_prime in range(num * num, n + 1, num):
            sieve[not_a_prime] = False

    return [num for num, is_prime in enumerate(sieve) if is_prime]

def max_prime_product(n):
    primes = prime_sieve(n // 2)

    lo, hi = 0, len(primes) - 1
    max_prod = 0
    max_pair = None

    while lo <= hi:
        prod = primes[lo] * primes[hi]
        if prod <= n:
            if prod > max_prod:
                max_prod = prod
                max_pair = (primes[lo], primes[hi])
            lo += 1
        else:
            hi -= 1
    return max_prod, max_pair

With your example this produces:

>>> max_prime_product(27)
(26, (2, 13))
Jaime
  • 65,696
  • 17
  • 124
  • 159
  • 1
    Actually, the time complexity of the scan phase of the algorithm is O(pi _n_) = O(_n_ / log _n_) because the scan is over the primes less than _n_ rather than the positive integers less than _n_. You could improve the run time of the algorithm by caching primes. – user448810 Aug 27 '15 at 00:53
  • Very true, have edited, thanks! Any clue or reference as to how does O(n / log n) compares to more usual complexities, e.g. O(n^k) with 0 < k < 1? If I remember my L'Hospital correctly, the limit as n goes to infinity of n^k / (n / log n) is 0, which I think could be interpreted as meaning that, asymptotically, O(n / log n) is no better than O(n). Does this make any sense? P.S. Love your website, thanks from putting it out every week from a silent, devout reader! – Jaime Aug 27 '15 at 01:22
  • @Jaime Sieve of Eratosthenes will find all primes < N/2. Most of these primes i believe may be discarded. What we would benefit further from is an effective **discard criteria**, to limit the candidate primes being considered. – Charles Okwuagwu Aug 27 '15 at 08:36
  • @Jaime: Asymptotically, O(_n_ / log _n_) is better than O(_n_). Thanks for the kind words; perhaps you could contribute a solution from time to time. Maybe even tomorrow. – user448810 Aug 27 '15 at 13:01