1

I need to find the greatest prime factor of a large number: up to 12 places (xxx,xxx,xxx,xxx). I have solved the problem, and the code works for small numbers (up to 6 places); however, the code won't run fast enough to not trigger a timeout on my server for something in the 100 billions.

I found a solution, thanks to all.

Code:

<?php   
    set_time_limit(300);

    function is_prime($number) {
        $sqrtn = intval(sqrt($number));
        //won't work for 0-2
            for($i=3; $i<=$sqrtn; $i+=2) {
                if($number%$i == 0) {
                    return false;
                }
            }
        return true;
    }   

    $initial = 600851475143;
    $prime_factors = array();

    for($i=3; $i<=9999; $i++) {
        $remainder = fmod($initial, $i);
        if($remainder == 0) {
            if(is_prime($i)) {
                $prime_factors[] = $i;
            }
        }
    }

    //print_r($prime_factors);
    echo "\n\n";
    echo "<b>Answer: </b>". max($prime_factors);    
?>

The test number in this case is 600851475143.

php_newb_88
  • 101
  • 1
  • 9
  • If you need **greatest** - you probably traverse from `$sqrti` to 2, not the opposite – zerkms Feb 21 '12 at 01:21
  • good point, can you loop in an anachronistic direction? – php_newb_88 Feb 21 '12 at 01:25
  • 1
    sure `for($i=$sqrti; $i>=2; $i--) {` – zerkms Feb 21 '12 at 01:28
  • 1
    This looks like project euler, the spirit is not to look for a solution until you solve it yourself. Or at least let people know it is from PE. – piotrm Feb 21 '12 at 01:30
  • @piotrm : thanks for the heads-up. I'll include that next time. It is a PE. Being so new to programming in general, I just wanted to use that as a springboard to learn. – php_newb_88 Feb 21 '12 at 01:32
  • @php_newb_88: if throwing a faster machine at it doesn't help, you can always parallelize and run it on several machines at the same time ;) – Niklas B. Feb 21 '12 at 01:38
  • http://stackoverflow.com/questions/453793/which-is-the-fastest-algorithm-to-find-prime-numbers – zerkms Feb 21 '12 at 01:45
  • @zerkms Not for this one. A sieve is totally not needed here. – Daniel Fischer Feb 21 '12 at 01:47
  • @Daniel Fischer: well, it is not that obvious for me. I don't see the reason to loop over billion items to check if the number is prime. – zerkms Feb 21 '12 at 01:50
  • @DanielFischer, good point, no idea why I was pointlessly looping. – php_newb_88 Feb 21 '12 at 01:51
  • @zerkms, thanks, I was just trying to write one myself "quick and dirty"...I wanted at least the original code to be that of my own work – php_newb_88 Feb 21 '12 at 01:52
  • that said, I think putting the `if(//number is 0, 1, or 2) {}` statements outside the for statement would help, because there is no need to loop. If the number is not 0, 1, or 2 it's not going to change on the next loop – php_newb_88 Feb 21 '12 at 01:54
  • 2
    @zerkms You don't need to loop over billions of items. Worst case for OP's way with the suggestions in my answer is `number_of_divisors * sqrt(n)`, which is a few million steps. If you do it right and get the prime factorisation, it's O(sqrt(n)) worst case. Sieving the primes to the square root and using only those takes more time than naive trial division (not to mention the space). – Daniel Fischer Feb 21 '12 at 02:12
  • @DanielFischer, I may have to improve the speed of my `is_prime($number)` function, because it's still too long – php_newb_88 Feb 21 '12 at 02:23
  • @php_newb_88 Hm, with my suggestions it runs in under half a second here. Although that's not fast for this problem, it's fast enough. – Daniel Fischer Feb 21 '12 at 02:33
  • @DanielFischer, hmm..I will scrub the code a few more times, because although it is running faster now, my output is incorrect `Array` `(` `[0] => 29` `[1] => 3` `)` something must be wrong on my end – php_newb_88 Feb 21 '12 at 03:30
  • Are you sure this is what you need? Usually the problems are designed in such a way that there is a much better solution than brute force. – harold Feb 21 '12 at 10:06
  • 1
    Minor optimization detail: swap `is_prime()` and `is_factor()` in the line `if ((is_prime($factor)) && (is_factor($number, $factor))) {` (do the cheap call first in the hope of avoiding the expensive call) – tom Feb 21 '12 at 13:24

2 Answers2

2

Your code will not find any prime factors larger than sqrt(n). To correct that, you have to test the quotient $number / $i also, for each factor (not only prime factors) found.

Your is_factor function

function is_factor($number, $factor) {
    $half = $number/2;
    for($y=1; $y<=$half; $y++) {
            if(fmod($number, $factor) == 0) {
            return true;
        }
    }
}

doesn't make sense. What's $y and the loop for? If $factor is not a divisor of $number, that will perform $number/2 utterly pointless divisions. With that fixed, reordering the tests in is_prime_factor will give a good speedup because the costly primality test needs only be performed for the few divisors of $number.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
1

Here is a really simple and fast solution.

LPF(n)
{
    for (i = 2; i <= sqrt(n); i++)
    {
        while (n > i && n % i == 0) n /= i;
    }

    return n;
}
tom
  • 21,844
  • 6
  • 43
  • 36