SPOJ prime generator in PHP - php

I am getting time limit exceeded error while I submitted my solution.
<?php
$stdin = fopen('php://stdin', 'r');
$testcase = fgets($stdin);
$n=1;
while($n<=$testcase){
$start = fgets($stdin);
$end=fgets($stdin);
if($start==1) {
$start=2;
}
for($i=$start;$i<$end;$i++){
$flag=0;
for($j=2;$j<$i;$j++){
if($i%$j==0){
$flag=1;
break;
}
}
if($flag==0){
echo $i."\n";
}
}
$n++;
}
fclose($stdin);
?>
Please help me out, I'm stuck with time limit exceeded error and please explain why I'm getting this error. In my local version it shows the correct answer.

Your solution is too slow. Try a slightly modified Sieve of Eratosthenes. The modification should allow your sieve to work only in the [start, end] interval.

In the first for loop, instead for looping all the numbers, you can loop only the odd numbers since other than 2 all the even numbers can never be prime.
you can use $i+=2 in first for loop. this could help you reduce some unnecessary looping.
Update 1:
Also inside second for loop, instead of finding modulo will all the numbers, you could just store the calculated prime numbers in an array and check whether the $i % calculated prime numbers == 0 instead of $i%$j

Related

In PHP i have used the logic to find the digits in a number, but i get a default output which is "325" instead of "4" for four digit number

Here is the Code below, please review and provide me a solution. It will be very helpful
<html>
<head>
<body>
<?php
$x=0;
$num=1000;
do{
$x++;
$num/=10;
}while($num!=0);
echo "$x";
?>
</body>
</html>
Your while condition is incorrect, you will never reach 0, well not until your float can't be evaluated accurately anymore. Since you are dividing a number ($num) by 10 and setting the result to $num on every iteration $num becoming closer and closer to zero. When you divide a non-zero number in this way the number gets closer and closer to zero, but will never actually reach 0 (You have discovered a limit to infinity). The reason your code does not cause an infinite loop and crash, is because PHP (and all languages) are limited to how small (or large) of a number a single variable can represent. Have a look at this PHP doc on floating point numbers for some more insight on float values and their possible accuracy. Once your $num value becomes small enough, PHP can no longer represent the value accurately leading to 0. Which as you discovered takes 325 iterations when starting at 1000.
You should instead check for when your $num becomes less then 1.
Example:
<?php
$x = 0;
$num = 1000;
do {
$x++;
$num /= 10;
} while($num >= 1);
echo $x;
Live Example
Using Original Condition
You could use your original while condition $num != 0 if you modify your calculation to round $num down to the nearest whole number. For example:
<?php
$x = 0;
$num = 1000;
do {
$x++;
$num = floor($num / 10);
} while($num != 0);
echo $x;
Live Example

Generating 3 unique random numbers in the range from 1 to 100

I wrote a code which is generating 3 random unique numbers from the range. It seems working, but I want to know is it working correctly and is my code suitable for a bigger range. For example, if I want to generate not 3, but 300 random unique numbers from a 1 to 10^6 range. How my code will perform in terms of memory usage and execution time?
The code is working, but I'm not sure about it. Just want to be sure, that I'm not missing something.
<?php
$array=range(1,100);
$rand = array_rand($array,3);
echo "Random number 1: ".$array[$rand[0]]."\n";
echo "Random number 2: ".$array[$rand[1]]."\n";
echo "Random number 3: ".$array[$rand[2]]."\n";
?>
As a result I want working code which is good in terms of performance.
Recursive function will help in this case
$r = f(1,1000000,300);
print_r($r);
function f($min, $max, $total, $current=[]){
if(count($current) == $total){
return $current;
}
$n = rand($min,$max);
in_array($n, $current) ? '' : ($current[] = $n);
return f($min, $max, $total, $current);
}

PHP Simple Math Results in Unexpected Result

I have two variables in a PHP program for billing statements, $charges and $payments.
$charges is the total amount due before any payments. $payments is the total amount received.
I calculate the balance due like so:
$balance_due = $charges-$payments;
Simple, except I am getting the following result:
$balance_due has -9.0949470177293E-13 for a value (expecting 0).
Both $charges and $payments have a value of 5511.53.
When I var_dump($charges) and var_dump($payments) they both show: float(5511.53)
This code (and === ):
if($charges == $payments){
error_log('they are the same');
}else{
error_log('they are not the same');
}
both result in false.
If I hard code: $charges = $payments = 5511.53; and run it then $balance_due = 0 as expected.
I am confused. What am I missing?
EDIT NOTES
I was able to use a user contributed function by Nitrogen found on the BC Math Functions page that was suggested I look at in order to come up with the following solution:
if(Comp($charges, $payments)===0){
$balance_due = 0;
}else{
$balance_due = ( $charges - $payments );
}
function Comp($Num1,$Num2,$Scale=null) {
// check if they're valid positive numbers, extract the whole numbers and decimals
if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');
// remove leading zeroes from whole numbers
$Num1=ltrim($Tmp1[1],'0');
$Num2=ltrim($Tmp2[1],'0');
// first, we can just check the lengths of the numbers, this can help save processing time
// if $Num1 is longer than $Num2, return 1.. vice versa with the next step.
if(strlen($Num1)>strlen($Num2)) return(1);
else {
if(strlen($Num1)<strlen($Num2)) return(-1);
// if the two numbers are of equal length, we check digit-by-digit
else {
// remove ending zeroes from decimals and remove point
$Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
$Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';
// if the user defined $Scale, then make sure we use that only
if($Scale!=null) {
$Dec1=substr($Dec1,0,$Scale);
$Dec2=substr($Dec2,0,$Scale);
}
// calculate the longest length of decimals
$DLen=max(strlen($Dec1),strlen($Dec2));
// append the padded decimals onto the end of the whole numbers
$Num1.=str_pad($Dec1,$DLen,'0');
$Num2.=str_pad($Dec2,$DLen,'0');
// check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than)
for($i=0;$i<strlen($Num1);$i++) {
if((int)$Num1{$i}>(int)$Num2{$i}) return(1);
else
if((int)$Num1{$i}<(int)$Num2{$i}) return(-1);
}
// if the two numbers have no difference (they're the same).. return 0
return(0);
}
}
}
That solution worked for me. The answer provided by imtheman below also works and seems more efficient so I am going to use that one instead. Is there any reason not to use one or the other of these?
The way I solved this problem when I ran into it was using php's number_format(). From php documentation:
string number_format(float $number [, int $decimals = 0 ])
So what I would do is this:
$balance_due = number_format($charges-$payments, 2);
And that should solve your problem.
Note: number_format() will return a string, so to compare it you must use == (not ===) or cast it back into a (float) before comparison.

Simple PHP hit counter / if else statement

I am trying to create a hit counter that once it reaches a certain number (in this case, 5), it will no longer display the amount of hits. This is my code:
<?php
$count = ("hits.txt");
$hits = file($count);
$hits[0] ++;
$fp = fopen($count , "w");
fputs($fp , "$hits[0]");
fclose($fp);
if ($hits > 5) {
echo "More than 5 hits";
}
else {
echo $hits[0];
}
?>
What am I doing wrong?
You are overcomplicating things. It would be much easier to do it like this:
$hits = file_get_contents('hits.txt');
++$hits;
file_put_contents('hits.txt', $hits);
if($hits > 5) {
echo 'Over 5!';
}
// else etc
As for your current code, the problem is that you don't test the number of hits with the correct syntax $hits[0] -- which you already use in fputs -- but with the wrong $hits instead. Remember that due to the way file works, $hits itself is an array. PHP will happily let you compare an array to an integer, and there are rules that define how the comparison works, but don't go there.
You need $hits[0] > 5:
if ($hits[0] > 5) {
echo "More than 5 hits";
}
The array value $hits when compared against a number 5 is compared as the string Array rather than the value of the array's first item. The string Array is always greater than 5.
More or less everything. In addition to other answers this
fputs($fp , "$hits[0]");
won't work as expected, you want either "{$hits[0]}" or $hits[0] (no quotes).
That is, if you don't care about concurrent access.

How to generate a random positive or negative decimal?

How can I regenerate random decimal from -0.0010 to 0.0010 with php rand() or some other method?
Divide rand() by the maximum random numer, multiply it by the range and add the starting number:
<?php
// rand()/getrandmax() gives a float number between 0 and 1
// if you multiply it by 0.002 you'll get a number between 0 and 0.002
// add the starting number -0.001 and you'll get a number between -0.001 and 0.001
echo rand()/getrandmax()*0.002-0.001;
?>
.
$val = (rand(0,20)-10)/10000;
This uses two rand() calls but I think that the readability makes up for it tenfold.
The first part makes either a -1 or +1. The second part can be anything between 0 and your limit for +/- numbers.
$rand = (rand(0,1)*2-1)*rand(0, 100);
echo $rand;
Unless you require LOTs of random numbers in a gigantic loop, you probably won't even notice the speed difference. I ran some tests (50.000 iterations) and it came up to around 0.0004 milliseconds to get a random number by my function. The alternatives are around half that time, but again, unless you are inside a really big loop, you are probably better of optimizing somewhere else.
Speed testing code:
$start = microtime();
$loopCount = 50000;
for($i=0;$i<$loopCount;$i++)
{
(0*2-1)*rand(0, 100);
}
$end = microtime();
echo "Timing: ", ((($end-$start)*1000.0)/((float)$loopCount)), " milliseconds.";
This will return any possible number between -0.001 and +0.001
$random = ((rand()*(0.002/getrandmax()))-0.001)
// or without paranthesis:
$random = rand()*0.002/getrandmax()-0.001
$randselect=rand(0,(array_sum($adarray)*100000000));
$cumilativevalue=0;
foreach ($adarray as $key => $value) {
$cumilativevalue=$cumilativevalue+$value*100000000;
if($randselect<$cumilativevalue){$selectedad=$key;break;}
}
Random float with one decimal between -1,1
$random = round((rand(0,1) - floatVal('0.'.rand(0,9).rand(0,9))), 1);

Categories