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.
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.
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;
}
Related
There is an algorithm for prime factorization in python. It runs in about 10 milliseconds for a big integer. I rewrote it for php. Also For very big integers I used bc and gmp functions in php. The result is very slow and takes about 4 seconds for the same input!
Here is my code:
(NOTE: the functions into the main function are tested separately and they are very fast)
public function primefactors($n, $sort = false) {
$smallprimes = $this->primesbelow(10000);
$factors = [];
// NOTE: bc or gmp functions is used for big numbers calculations
$limit = bcadd( bcsqrt($n) , 1);
foreach ($smallprimes as $checker) {
if ($checker > $limit) {
break;
}
// while (gmp_mod($n, $checker) == 0) {
// while ($n%$checker == 0) {
while ( bcmod($n, $checker) == 0 ) {
array_push($factors, $checker);
// $n = (int)($n/$checker);
$n = bcdiv($n, $checker);
// $limit = (int)(bcpow($n, 0.5)) + 1;
$limit = bcadd( bcsqrt($n) , 1);
if ($checker > $limit) {
break;
}
}
}
if ($n < 2) {
return $factors;
}
while ($n > 1) {
if ($this->isprime($n)) {
array_push($factors, $n);
// var_dump($factors);
break;
}
$factor = $this->pollard_brent($n);
$factors = array_merge($factors, $this->primefactors($factor));
$n = (int)($n/$factor);
}
if ($sort) {
sort($factors);
}
return $factors;
}
Is there any performance issue in my code?? Or php itself has performance issue? Why python is so fast? (About 40 times faster)
Edit: Here is the python code:
smallprimes = primesbelow(10000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
factors = []
limit = int(n ** .5) + 1
for checker in smallprimes:
if checker > limit: break
while n % checker == 0:
factors.append(checker)
n //= checker
limit = int(n ** .5) + 1
if checker > limit: break
if n < 2: return factors
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
Check this benchmarks https://blog.famzah.net/2016/02/09/cpp-vs-python-vs-perl-vs-php-performance-benchmark-2016/
You are asking why a turttle is slower than a horse doing the same circuit.
I have the following code to find the largest prime factors of a number, it works good if I use numbers from 11 digits, but when I use this number: 600851475143, it keeps loading and loading and just don't show the result.
Any advice is welcome
<?php
$number = 600851475143;
$prime = 2;
do {
if($number % $prime == 0) {
$number = $number / $prime;
$primes[] = $prime;
} else {
while(true) {
$prime++;
$true = is_prime($prime);
if($true === true) {
break;
}
}
}
if($number < $prime) break;
} while(true);
function is_prime($number, $prime = 2) {
if($number % $prime == 0) {
return false;
} else if ($number % $prime++ == 0) {
return false;
}
return true;
}
var_dump(max($primes));
?>
It should not be related to the overflow problem, but I think your is_prime() function, doesn't work in fact
var_dump(is_prime(9)); // bool(true)
Anyway to handle arbitrary precision numbers in PHP you should look here
This is a program to find prime factors of a numbers.
Like - Prime factors for number 1125 are 3 and 5
My Algo this goes way - (and please let me know if it is not correct)
Firstly I am finding a square root of the number using sqrt()
function to break the complexity and run time.
To find prime numbers in between the range.
Lastly to divide these prime numbers with the original number(yet not reached to this step as failing in the second step.
My code which is not working, let me know where exactly I am failing in my logic and code from step 2 and step 3.
No error thrown but the code is also not outputting anything.
<?php
error_reporting(E_ALL);
$number = 6006;
$sqrt_num = (int)sqrt($number);
for($i=2;$i<$sqrt_num;$i++)
{
for($j=2;$j<=$i-1;$j++)
{
if($i%$j==0)
break;
if($i==$j)
echo $i;
}
}
Since I'm note sure if you want prime factors of a number, or all prime numbers within the range of 1 to sqrt(number), I'll give you some of my code that I wrote a while back and show different implementations:
//Check if a number is prime
function isPrime($num, $pf = null)
{
if(!is_array($pf))
{
for($i=2;$i<intval(sqrt($num));$i++) {
if($num % $i==0) {
return false;
}
}
return true;
} else {
$pfCount = count($pf);
for($i=0;$i<$pfCount;$i++) {
if($num % $pf[$i] == 0) {
return false;
}
}
return true;
}
}
//Find Prime Factors
function primeFactors($num)
{
//Record the base
$base = intval($num/2);
$pf = array();
$pn = null;
for($i=2;$i <= $base;$i++) {
if(isPrime($i, $pn)) {
$pn[] = $i;
while($num % $i == 0)
{
$pf[] = $i;
$num = $num/$i;
}
}
}
return $pf;
}
From that, to get the prime factors, just use $myarr = primeFactors($number); (and you can see the logic used to recreate your own. ^^)
If you want all prime numbers in the range:
for($i=1;$i<$sqrt_num;$i++) {
if(isPrime($i)) {
$myarr[] = $i;
}
}
I do want to note the use of $pf in isPrime, as it is the sieve to reduce the processing time of finding out if a number is prime based on the prime factors already processed.
Finding prime numbers is a common problem in most programming languages. First, take a look on number is prime. You can use the gmp_nextprime function.
$namber = 1122676330;
$text = $namber.'=';
$time_start = microtime(1);
for($i=2; $i<=$namber; $i++ ){
if($namber % $i == 0){
$namber = $namber / $i;
$text .= $i. ' ';
$i--;
}
if($namber == 1){
echo 'ok';
}
}
$time_end = microtime(1);
$time = $time_end - $time_start;
echo "<br><br>$text<br><br> $time seconds\n";
you can use php class on PrimeModule
PHP Prime module capable of doing prime factorization of huge numbers very quickly.
link:
https://github.com/danog/PrimeModule
I'm trying to program my own Sine function implementation for fun but I keep getting :
Fatal error: Maximum execution time of 30 seconds exceeded
I have a small HTML form where you can enter the "x" value of Sin(x) your looking for and the number of "iterations" you want to calculate (precision of your value), the rest is PhP.
The maths are based of the "Series definition" of Sine on Wikipedia :
--> http://en.wikipedia.org/wiki/Sine#Series_definition
Here's my code :
<?php
function factorial($int) {
if($int<2)return 1;
for($f=2;$int-1>1;$f*=$int--);
return $f;
};
if(isset($_POST["x"]) && isset($_POST["iterations"])) {
$x = $_POST["x"];
$iterations = $_POST["iterations"];
}
else {
$error = "You forgot to enter the 'x' or the number of iterations you want.";
global $error;
}
if(isset($x) && is_numeric($x) && isset($iterations) && is_numeric($iterations)) {
$x = floatval($x);
$iterations = floatval($iterations);
for($i = 0; $i <= ($iterations-1); $i++) {
if($i%2 == 0) {
$operator = 1;
global $operator;
}
else {
$operator = -1;
global $operator;
}
}
for($k = 1; $k <= (($iterations-(1/2))*2); $k+2) {
$k = $k;
global $k;
}
function sinus($x, $iterations) {
if($x == 0 OR ($x%180) == 0) {
return 0;
}
else {
while($iterations != 0) {
$result = $result+(((pow($x, $k))/(factorial($k)))*$operator);
$iterations = $iterations-1;
return $result;
}
}
}
$result = sinus($x, $iterations);
global $result;
}
else if(!isset($x) OR !isset($iterations)) {
$error = "You forgot to enter the 'x' or the number of iterations you want.";
global $error;
}
else if(isset($x) && !is_numeric($x)&& isset($iterations) && is_numeric($iterations)) {
$error = "Not a valid number.";
global $error;
}
?>
My mistake probably comes from an infinite loop at this line :
$result = $result+(((pow($x, $k))/(factorial($k)))*$operator);
but I don't know how to solve the problem.
What I'm tring to do at this line is to calculate :
((pow($x, $k)) / (factorial($k)) + (((pow($x, $k))/(factorial($k)) * ($operator)
iterating :
+ (((pow($x, $k))/(factorial($k)) * $operator)
an "$iterations" amount of times with "$i"'s and "$k"'s values changing accordingly.
I'm really stuck here ! A bit of help would be needed. Thank you in advance !
Btw : The factorial function is not mine. I found it in a PhP.net comment and apparently it's the optimal factorial function.
Why are you computing the 'operator' and power 'k' out side the sinus function.
sin expansion looks like = x - x^2/2! + x^3/3! ....
something like this.
Also remember iteration is integer so apply intval on it and not floatval.
Also study in net how to use global. Anyway you do not need global because your 'operator' and power 'k' computation will be within sinus function.
Best of luck.
That factorial function is hardly optimal—for speed, though it is not bad. At least it does not recurse. It is simple and correct though. The major aspect of the timeout is that you are calling it a lot. One technique for improving its performance is to remember, in a local array, the values for factorial previously computed. Or just compute them all once.
There are many bits of your code which could endure improvement:
This statement:
while($iterations != 0)
What if $iterations is entered as 0.1? Or negative. That would cause an infinite loop. You can make the program more resistant to bad input with
while ($iterations > 0)
The formula for computing a sine uses the odd numbers: 1, 3, 5, 7; not every integer
There are easier ways to compute the alternating sign.
Excess complication of arithmetic expressions.
return $result is within the loop, terminating it early.
Here is a tested, working program which has adjustments for all these issues:
<?php
// precompute the factorial values
global $factorials;
$factorials = array();
foreach (range (0, 170) as $j)
if ($j < 2)
$factorials [$j] = 1;
else $factorials [$j] = $factorials [$j-1] * $j;
function sinus($x, $iterations)
{
global $factorials;
$sign = 1;
for ($j = 1, $result = 0; $j < $iterations * 2; $j += 2)
{
$result += pow($x, $j) / $factorials[$j] * $sign;
$sign = - $sign;
}
return $result;
}
// test program to prove functionality
$pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620;
$x_vals = array (0, $pi/4, $pi/2, $pi, $pi * 3/2, 2 * $pi);
foreach ($x_vals as $x)
{
$y = sinus ($x, 20);
echo "sinus($x) = $y\n";
}
?>
Output:
sinus(0) = 0
sinus(0.78539816339745) = 0.70710678118655
sinus(1.5707963267949) = 1
sinus(3.1415926535898) = 3.4586691443274E-16
sinus(4.7123889803847) = -1
sinus(6.2831853071796) = 8.9457384260403E-15
By the way, this executes very quickly: 32 milliseconds for this output.
I got the answer fine, but when I run the following code,
$total = 0;
$x = 0;
for ($i = 1;; $i++)
{
$x = fib($i);
if ($x >= 4000000)
break;
else if ($x % 2 == 0)
$total += $x;
print("fib($i) = ");
print($x);
print(", total = $total");
}
function fib($n)
{
if ($n == 0)
return 0;
else if ($n == 1)
return 1;
else
return fib($n-1) + fib($n-2);
}
I get the warning that I have exceeded the maximum execution time of 30 seconds. Could you give me some pointers on how to improve this algorithm, or pointers on the code itself? The problem is presented here, by the way.
Let's say $i equal to 13. Then $x = fib(13)
Now in the next iteration, $i is equal to 14, and $x = fib(14)
Now, in the next iteration, $i = 15, so we must calculate $x. And $x must be equal to fib(15). Now, wat would be the cheapest way to calculate $x?
(I'm trying not to give the answer away, since that would ruin the puzzle)
Try this, add caching in fib
<?
$total = 0;
$x = 0;
for ($i = 1;; $i++) {
$x = fib($i);
if ($x >= 4000000) break;
else if ($x % 2 == 0) $total += $x;
print("fib($i) = ");
print($x);
print(", total = $total\n");
}
function fib($n) {
static $cache = array();
if (isset($cache[$n])) return $cache[$n];
if ($n == 0) return 0;
else if ($n == 1) return 1;
else {
$ret = fib($n-1) + fib($n-2);
$cache[$n] = $ret;
return $ret;
}
}
Time:
real 0m0.049s
user 0m0.027s
sys 0m0.013s
You'd be better served storing the running total and printing it at the end of your algorithm.
You could also streamline your fib($n) function like this:
function fib($n)
{
if($n>1)
return fib($n-1) + fib($n-2);
else
return 0;
}
That would reduce the number of conditions you'd need to go through considerably.
** Edited now that I re-read the question **
If you really want to print as you go, use the output buffer. at the start use:
ob_start();
and after all execution, use
ob_flush();
flush();
also you can increase your timeout with
set_time_limit(300); //the value is seconds... so this is 5 minutes.