PHP for loop displaying strange results - php

I have this code:
for($i = 1; $i <= $max; $i+=0.1) {
echo "$i<br>";
}
if the variable $max = 6; the results are: 1, 1.1, 1.2, 1.3 .... 5.8, 5.9, 6 , but when the variable $max = 4 the results are: 1, 1.1 ... 3.8, 3.9, but the number 4 is missing.
Please explain this behavior, and a possible solution to this.
the results are the same when i use the condition $i <= $max; or $i < $max;
The bug occurs when $max is 2, 3 or 4

From http://php.net/manual/en/language.types.float.php
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision.
So to overcome this you could multiply your numbers by 10.
So $max is 40 or 60.
for($i = 10; $i <= $max; $i+=1) {
echo ($i/10).'<br>';
}

$err = .000001//allowable error
for($i = 1; $i <= $max+$err; $i+=0.1) {
echo "$i<br>";
}

You can use of number_format()
<?php
$max=6;
for($i = 1; number_format($i,2) < number_format($max,2); $i+=0.1) {
echo $i."<br>";
}
?>

You should set the precision when using integers,
like this:
$e = 0.0001;
while($i > 0) {
echo($i);
$i--;
}

Related

How can I make a counter in PHP that counts up to 10 (including "first decimal-place" numbers)?

I'm trying to make a PHP counter and this is what I have:
<?PHP
for ($i = 0; $i < 10; $i ++) {
print "$i";
}
?>
It's quite a simple counter but I was wondering if it could count all the decimal numbers (only one decimal place) as well, is there any way I could do that?
This is what I would like:
0.1,
0.2,
0.3,
...
9.8,
9.9,
10.0.
It's impossible to count from 1 to 10, including ALL decimals. There would be infinitely many of them.
However, you could count in tenths:
<?php
for ($i = 0; $i <= 10; $i+=0.1) {
print "$i\r\n";
}
?>
You need to decide on a precision. Let's say you want a decimal precision of 2 decimal places for this exercise (but this could obviously be changed in code). My suggestion would be to simply implement an integer counter between 0 and 1000 and then divide counter value by 100 to get decimal values.
$decimal_precision = 2;
$counter_limit = 10;
$counter_ratio = (int)pow(10, $decimal_precision);
$integer_limit = $counter_limit * $counter_ratio;
for($i = 1; $i <= $integer_limit; $i++) {
echo (float) $i / $counter_ratio;
}

Why is the last number (1) printed?

The code:
<?php
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while ($i < $stop) {
echo($i . "<br/>");
$i += $step;
}
?>
The output:
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1 <-- notice the 1 printed when it shouldn't
Created a fiddle
One more: if you set $start = 1 and $stop = 2 it works fine.
Using: php 5.3.27
Why is the 1 printed?
Because not only float math is flawed, sometimes its representation is flawed too - and that's the case here.
You don't actually get 0.1, 0.2, ... - and that's quite easy to check:
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while ($i < $stop) {
print(number_format($i, 32) . "<br />");
$i += $step;
}
The only difference here, as you see, is that echo replaced with number_format call. But the results are drastically different:
0.10000000000000000555111512312578
0.20000000000000001110223024625157
0.30000000000000004440892098500626
0.40000000000000002220446049250313
0.50000000000000000000000000000000
0.59999999999999997779553950749687
0.69999999999999995559107901499374
0.79999999999999993338661852249061
0.89999999999999991118215802998748
0.99999999999999988897769753748435
See? Only one time it was 0.5 actually - because that number can be stored in a float container. All the others were only approximations.
How to solve this? Well, one radical approach is using not floats, but integers in similar situations. It's easy to notice that have you done it this way...
$start = 0;
$stop = 10;
$step = (int)(($stop - $start) / 10);
$i = $start + $step;
while ($i < $stop) {
print(number_format($i, 32) . "<br />");
$i += $step;
}
... it would work ok:
Alternatively, you can use number_format to convert the float into some string, then compare this string with preformatted float. Like this:
$start = 0;
$stop = 1;
$step = ($stop - $start) / 10;
$i = $start + $step;
while (number_format($i, 1) !== number_format($stop, 1)) {
print(number_format($i, 32) . "\n");
$i += $step;
}
The problem is that the number in the variable $i is not 1 (when printed). Its actual just less than 1. So in the test ($i < $stop) is true, the number is converted to decimal (causing rounding to 1), and displayed.
Now why is $i not 1 exactly? It is because you got there by saying 10 * 0.1, and 0.1 cannot be represented perfectly in binary. Only numbers which can be expressed as a sum of a finite number of powers of 2 can be perfectly represented.
Why then is $stop exactly 1? Because it isn't in floating point format. In other words, it is exact from the start -- it isn't calculated within the system used floating point 10 * 0.1.
Mathematically, we can write this as follows:
A 64 bit binary float can only hold the first 27 non-zero terms of the sum which approximates 0.1. The other 26 bits of the significand remain zero to indicate zero terms. The reason 0.1 isn't physically representable is that the required sequence of terms is infinite. On the other hand, numbers like 1 require only a small finite number of terms and are representable. We'd like that to be the case for all numbers. This is why decimal floating point is such an important innovation (not widely available yet). It can represent any number that we can write down, and do so perfectly. Of course, the number of available digits remains finite.
Returning to the given problem, since 0.1 is the increment for the loop variable and isn't actually representable, the value 1.0 (although representable) is never precisely reached in the loop.
If your step will always be a factor of 10, you can accomplish this quickly with the following:
<?php
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while (round($i, 1) < $stop) { //Added round() to the while statement
echo($i . "<br/>");
$i += $step;
}
?>

PHP make combination with exponents

I've been looking in to a math problem: to find the factors of a large number.
I've come to the method of "prime factorization", that all went well to code in php.
But then, say i want to know the factors of the number 196 (being: 1, 2, 4, 7, 14, 28, 49, 98, 196), i've found that the prime factorization of this number is: (2^2)(7^2).
To find the factors, you'll have to make all the possible combinations between the two and muttiply them:
(2^0)(7^0) = 1
(2^1)(7^0) = 2
(2^2)(7^0) = 4
(2^0)(7^1) = 7
(2^1)(7^1) = 14
(2^2)(7^1) = 28
(2^0)(7^2) = 49
(2^1)(7^2) = 98
(2^2)(7^2) = 196
This is where i'm stuck.
I need to find a function that will make a combination of these items (the exponent may not be higher then the one is the prime factorization of that number). This function will have to work on N factors (N is a number > 0 and smaller than 100).
I hope you understand my problem and have some ideas on how to solve it!
The following will print the prime factor of $N = '196' along with the number of exponents.
<?php
function is_prime($N) {
if($N == 2) return true;
for($i = 3 ; $i * $i <= $N; $i++ ) {
if($N % $i == 0) return false;
}
return true;
}
$a = array();
$N = '196';
for($i = 2; $i <= $N; $i++) {
while($N % $i == 0 && is_prime($i)) {
if(!isset($a[$i])) {
$a[$i] = 0;
}
$a[$i]++;
$N = $N / $i;
}
}
print_r($a);
?>

Project Euler #23: Non-abundant sums

I'm struggling with Project Euler problem 23: Non-abundant sums.
I have a script, that calculates abundant numbers:
function getSummOfDivisors( $number )
{
$divisors = array ();
for( $i = 1; $i < $number; $i ++ ) {
if ( $number % $i == 0 ) {
$divisors[] = $i;
}
}
return array_sum( $divisors );
}
$limit = 28123;
//$limit = 1000;
$matches = array();
$k = 0;
while( $k <= ( $limit/2 ) ) {
if ( $k < getSummOfDivisors( $k ) ) {
$matches[] = $k;
}
$k++;
}
echo '<pre>'; print_r( $matches );
I checked those numbers with the available on the internet already, and they are correct. I can multiply those by 2 and get the number that is the sum of two abundant numbers.
But since I need to find all numbers that cannot be written like that, I just reverse the if statement like this:
if ( $k >= getSummOfDivisors( $k ) )
This should now store all, that cannot be created as the sum of to abundant numbers, but something is not quit right here. When I sum them up I get a number that is not even close to the right answer.
I don't want to see an answer, but I need some guidelines / tips on what am I doing wrong ( or what am I missing or miss-understanding ).
EDIT: I also tried in the reverse order, meaning, starting from top, dividing by 2 and checking if those are abundant. Still comes out wrong.
An error in your logic lies in the line:
"I can multiply those by 2 and get the number that is the sum of two abundant numbers"
You first determine all the abundant numbers [n1, n2, n3....] below the analytically proven limit. It is then true to state that all integers [2*n1, 2*n2,....] are the sum of two abundant numbers but n1+n2, and n2+n3 are also the sum of two abundant numbers. Therein lies your error. You have to calculate all possible integers that are the sum of any two numbers from [n1, n2, n3....] and then take the inverse to find the integers that are not.
I checked those numbers with the available on the internet already, and they are correct. I can multiply those by 2 and get the number that is the sum of two abundant numbers.
No, that's not right. There is only one abundant number <= 16, but the numbers <= 32 that can be written as the sum of abundant numbers are 24 (= 12 + 12), 30 (= 12 + 18), 32 (= 12 + 20).
If you have k numbers, there are k*(k+1)/2 ways to choose two (not necessarily different) of them. Often, a lot of these pairs will have the same sum, so in general there are much fewer than k*(k+1)/2 numbers that can be written as the sum of two of the given k numbers, but usually, there are more than 2*k.
Also, there are many numbers <= 28123 that can be written as the sum of abundant numbers only with one of the two abundant numbers larger than 28123/2.
This should now store all, that cannot be created as the sum of to abundant numbers,
No, that would store the non-abundant numbers, those may or may not be the sum of abundant numbers, e.g. 32 is a deficient number (sum of all divisors except 32 is 31), but can be written as the sum of two abundant numbers (see above).
You need to find the abundant numbers, but not only to half the given limit, and you need to check which numbers can be written as the sum of two abundant numbers. You can do that by taking all pairs of two abundant numbers (<= $limit) and mark the sum, or by checking $number - $abundant until you either find a pair of abundant numbers or determine that none sums to $number.
There are a few number theoretic properties that can speed it up greatly.
Below is php code takes 320 seconds
<?php
set_time_limit(0);
ini_set('memory_limit', '2G');
$time_start = microtime(true);
$abundantNumbers = array();
$sumOfTwoAbundantNumbers = array();
$totalNumbers = array();
$limit = 28123;
for ($i = 12; $i <= $limit; $i++) {
if ($i >= 24) {
$totalNumbers[] = $i;
}
if (isAbundant($i)) {
$abundantNumbers[] = $i;
}
}
$countOfAbundantNumbers = count($abundantNumbers);
for ($j = 0; $j < $countOfAbundantNumbers; $j++) {
if (($j * 2) > $limit)
break; //if sum of two abundant exceeds limit ignore that
for ($k = $j; $k < $countOfAbundantNumbers; $k++) { //set $k = $j to avoid duble addtion like 1+2, 2+1
$l = $abundantNumbers[$j] + $abundantNumbers[$k];
$sumOfTwoAbundantNumbers[] = $l;
}
}
$numbers = array_diff($totalNumbers, $sumOfTwoAbundantNumbers);
echo '<pre>';print_r(array_sum($numbers));
$time_end = microtime(true);
$execution_time = ($time_end - $time_start);
//execution time of the script
echo '<br /><b>Total Execution Time:</b> ' . $execution_time . 'seconds';
exit;
function isAbundant($n) {
if ($n % 12 == 0 || $n % 945 == 0) { //first even and odd abundant number. a multiple of abundant number is also abundant
return true;
}
$k = round(sqrt($n));
$sum = 1;
if ($n >= 1 && $n <= 28123) {
for ($i = 2; $i <= $k; $i++) {
if ($n % $i == 0)
$sum+= $i + ( $n / $i);
if ($n / $i == $i) {
$sum = $sum - $i;
}
}
}
return $sum > $n;
}

The largest prime factor with php

I wrote a program in PHP to find the largest prime factor. I think it is quite optimized, because it loads quite fast. But, there is a problem: it doesn't count the prime factors of very big numbers. Here is the program:
function is_even($s) {
$sk_sum = 0;
for($i = 1; $i <= $s; $i++) {
if($s % $i == 0) { $sk_sum++; }
}
if($sk_sum == 2) {
return true;
}
}
$x = 600851475143; $i = 2; //x is number
while($i <= $x) {
if($x % $i == 0) {
if(is_even($i)) {
$sk = $i; $x = $x / $i;
}
}
$i++;
}
echo $sk;
The largest non-overflowing integer in PHP is stored in the constant PHP_INT_MAX.
You won't be able to work with integers larger than this value in PHP.
To see all of PHP's predefined constants, just use:
<?php
echo '<pre>';
print_r(get_defined_constants());
echo '</pre>';
?>
PHP_INT_MAX probably has a value of 2,147,483,647.
To handle numbers of arbitrary precision in PHP, see either the GMP or BC Math PHP extensions.
You should read about Prime testing and Sieving.
In particular, you don't need to test whether each of your divisors is prime.
Something like the following would be faster.
while($i <= $x)
{
while ($x % $i == 0)
{
$sk = $i;
$x = $x / $i;
}
$i++;
}
You can also stop your outer loop when $i reaches sqrt($x), and if you haven't found a divisor yet then you know $x is prime.
Well, every language has it's own (while usually same) limitations, so if you exceed this php's limit, you can't get any higher. Max Integer is 9E18.

Categories