PHP, Generating Primes, What's Wrong - php

What's wrong with this? The conditional statement looks solid enough, yet all numbers are included
function generate_primes(){
$max = 100;
$primes = array();
for($current_pointer = 1; $current_pointer <= $max; $current_pointer++){
for($divider = 1; $divider <= $current_pointer; $divider++){
//if(in_array($divider, $primes)){
if(($current_pointer % $divider === 0) && ($divider !== 1) && ($divider === $current_pointer)){
$primes[] = $current_pointer;
}
//}
}
}
print_r($primes);
}
generate_primes();

You need to deal with the case that your divider does divide evenly into your current_pointer but the divider doesn't equal the current pointer. In that case, you need to jump out of the loop (i.e. you've found something that divides evenly, so the number isn't prime). As written, all loops eventually hit the case of dividing the number by itself, so all numbers succeed.
In other words, you're trying to test for the FIRST successful divider being the number itself, but to do that, you have to stop trying when you hit a different successful divider.
if(($current_pointer % $divider === 0) && ($divider !== 1)){
if ($divider === $current_pointer)
$primes[] = $current_pointer;
} else {
continue; // the continue makes you stop testing the current pointer and go on to the next
}

Re-think your algorithm. A prime number is a natural number > 1 that is not evenly divisible by any other natural number except itself and 1. So, if $current_pointer % $divider === 0, the number in question can't be a prime number, becauseit is divisible by $divider. So why do you add it to the array?
Edit in response to comments: To clarify, this means that you have to check for all possible divisors (from 2 to the number itself -1) and make sure that not a single of them divides the number in question without a remainder. In and only this case the number is a prime.
You can optimize this algorithm slightly (i.e. the root of your number can be the upper bound for the inner loop), but first try to get things working in the basic case.

I believe your last condition should be:
$divider !== $current_pointer

Two wrong things: 1) $divider === $current_pointer) should be $divider != $current_pointer) and 2) you are adding the number to the $primes array after each and every iteration.
Suggestions: 1) start the second loop by 2, since you want $divider not to be 1 in the first place, and 2) make $divider < $current_pointer (instead of <=), since you also want $divider not to be itself.
Try this:
function generate_primes(){
$max = 100;
$primes = array();
for($current_pointer = 1; $current_pointer <= $max; $current_pointer++){
$prime = true;
for($divider = 2; $divider < $current_pointer; $divider++){
if($current_pointer % $divider == 0) $prime = false;
}
if($prime == true) array_push($primes, $current_pointer);
}
print_r($primes);
}
generate_primes();

First off, your loop is a bit funny ;) If you want to exclude 1, don't start with 1
Second, I don't understand the last clause of your conditional. You are saying you will only consider it a prime if the divider is === to current pointer. I think you mean != to current pointer.

Look at your if statement. It essentially says:
Mark a number as prime if:
current_pointer equals 0.
divider doesn't equal 0.
divider equals current_pointer.
The only time all 3 of those conditionals will be true is when the final conditional is true (divider equals current_pointer.)
If the current pointer is 6, then when the divider equals 6, you'll store it as a prime (obviously not true.)
See this example as a simple (although expensive implementation) function for testing if a number is prime (you could easily use the logic to make a generator.)
Prime number checker

I'd suggest you look up the Sieve of Eratosthene algorithm. It's for getting a list of primes, and is very fast compared to the brute force approach you are taking. While your code will run fine for smaller numbers, you'll find as you try to, for example, get all primes < 10,000,000 it will take a long time.

The problem is that
if( ($current_pointer % $divider === 0) &&
($divider !== 1) &&
($divider === $current_pointer)){
will always be true when $divider == $current_pointer
A better approach is to use a is_prime flag initiated to true. Set it to false if any number smaller than it divides it.
A further enhancement would be to add the number two, and then only check non even numbers.
function generate_primes($max = 100){
$primes = array(2);
for($current_pointer = 3; $current_pointer <= $max; $current_pointer += 2){
if(is_odd_number_a_prime($current_pointer)){
$primes[] = $current_pointer;
}
print_r($primes);
return $primes
}
function is_odd_number_a_prime($n){
if(is_int($n)){
for($divider = 3; $divider < $n; $divider +=2 ){
if($current_pointer % $n == 0){
return false;
}
}
}else{
return false;
}
return true;
}
$primes = generate_primes();
You could further enhance things by using a sieve.

Related

For loop and if statement every few times

I have for loop from 1 to 6000 and I would like to execute a function every 100 times. I have only such an idea:
for($i=0;$i<=6000;$i++)
{
if($i==100 && $i==200 ... $i==6000) do something;
}
How can I solve this problem differently?
From http://php.net/manual/en/language.operators.arithmetic.php
if ($i % 100 == 0) {
// $i can be fully divided by 100
}
The modulo operator (%) tells you if a number divided by another number has a remainder. If the remainder is 0, you know the first number is a multiple of the second (since it divides evenly).
Just check if i is a multiple of 100:
for($i=0;$i<=6000;$i++)
{
if($i % 100 == 0) {
}
}
I agree with the answers this question has received already. However, you might want to omit the case when $i is 0. So you can check it in your for loop if you are starting from 0.
for($i=0; $i<=6000; $i++)
{
if($i != 0 && $i % 100 == 0){
// do something
}
}

PHP function that checks for matching numbers

I am currently teaching myself web development/ programming and to learn php i have built a simple program. The program takes user input and based on a series of math algorithms and calculates 7 random lottery numbers. The code is working fine but i want to improve it. The code is very repetitive and i want to simplify it by creating my own functions. I have created the first function that takes the users input, simply does some maths and then returns some values.
For Example...
<?php
function some_maths($int1 $int2 $int3){
$x = $int1 + $int2;
$y = $int2 * $int3;
$z = $y * $x;
return $x
....}
So this is pretty straight forward, but what i want to do now is take the values of X, Y, Z and create a function that checks to make sure they're not matching, or that they're not less than 1 or greater than 59. I used a while loop in my original code that goes like this:
while($x == $y || $x == $z || $x <1 || $x >59){
if( x> 59 || x < 1){
if (x<1){
do{ $x+=$int}while($x <1);
}elseif ($x > 59){
do{ $x-=$int}while($x >59);
}else $x++;
}
This seems to work fine but i don't want to have to repeat the same code over and over. I am sure there has to be a better way? Could i put the values into an array and maybe do it that way? What would be the best solution for this?
Your question is kind of vague but if I had to write a function to check if three numbers weren't equal and were < 59 and >1 this is how I would do it
function validateNumbers($x , $y , $z)
{
if(equal($x,$y)) return false;
if(equal($x,$z)) return false;
if(equal($y,$z)) return false;
if($x>59||$x<1) return false;
if($y>59||$y<1) return false;
if($z>59||$z<1) return false;
return true;
}
function equal($x , $y)
{
if($x == $y)return true;
else return fasle;
}
So far I only see two (pretty straightforward) things:
Your function prototype in the first example is missing commas between the parameters. Instead of function some_maths($int1 $int2 $int3) it should read function some_maths($int1, $int2, $int3).
In your second example a closing } is missing. But if I am interpreting your stuff correctly, the outer if-clause is redundant. Thus, the snippet can be simplified to:
Second example:
while($x == $y || $x == $z || $x <1 || $x >59){
if (x<1){
do{ $x+=$int}while($x <1);
}
elseif ($x > 59){
do{ $x-=$int}while($x >59);
}
else $x++;
}
There may be more room for improvement (e.g. slim down the condition of the outer while loop) - but for that we would need more context (what happens before your loop, what is $int, ...).

Optimal way of cycling through 1000's of values

I need to find the value of x where the variance of two results (which take x into account) is the closest to 0. The problem is, the only way to do this is to cycle through all possible values of x. The equation uses currency, so I have to check in increments of 1 cent.
This might make it easier:
$previous_var = null;
$high_amount = 50;
for ($i = 0.01; $i <= $high_amount; $i += 0.01) {
$val1 = find_out_1($i);
$val2 = find_out_2();
$var = variance($val1, $val2);
if ($previous_var == null) {
$previous_var = $var;
}
// If this variance is larger, it means the previous one was the closest to
// 0 as the variance has now started increasing
if ($var > $previous_var) {
$l_s -= 0.01;
break;
}
}
$optimal_monetary_value = $i;
I feel like there is a mathematical formula that would make the "cycling through every cent" more optimal? It works fine for small values, but if you start using 1000's as the $high_amount it takes quite a few seconds to calculate.
Based on the comment in your code, it sounds like you want something similar to bisection search, but a little bit different:
function calculate_variance($i) {
$val1 = find_out_1($i);
$val2 = find_out_2();
return variance($val1, $val2);
}
function search($lo, $loVar, $hi, $hiVar) {
// find the midpoint between the hi and lo values
$mid = round($lo + ($hi - $lo) / 2, 2);
if ($mid == $hi || $mid == $lo) {
// we have converged, so pick the better value and be done
return ($hiVar > $loVar) ? $lo : $hi;
}
$midVar = calculate_variance($mid);
if ($midVar >= $loVar) {
// the optimal point must be in the lower interval
return search($lo, $loVar, $mid, $midVar);
} elseif ($midVar >= $hiVar) {
// the optimal point must be in the higher interval
return search($mid, $midVar, $hi, $hiVar);
} else {
// we don't know where the optimal point is for sure, so check
// the lower interval first
$loBest = search($lo, $loVar, $mid, $midVar);
if ($loBest == $mid) {
// we can't be sure this is the best answer, so check the hi
// interval to be sure
return search($mid, $midVar, $hi, $hiVar);
} else {
// we know this is the best answer
return $loBest;
}
}
}
$optimal_monetary_value = search(0.01, calculate_variance(0.01), 50.0, calculate_variance(50.0));
This assumes that the variance is monotonically increasing when moving away from the optimal point. In other words, if the optimal value is O, then for all X < Y < O, calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) (and the same with all > and < flipped). The comment in your code and the way have you have it written make it seem like this is true. If this isn't true, then you can't really do much better than what you have.
Be aware that this is not as good as bisection search. There are some pathological inputs that will make it take linear time instead of logarithmic time (e.g., if the variance is the same for all values). If you can improve the requirement that calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) to be calculate_variance(X) > calculate_variance(Y) > calculate_variance(O), you can improve this to be logarithmic in all cases by checking to see how the variance for $mid compares the the variance for $mid + 0.01 and using that to decide which interval to check.
Also, you may want to be careful about doing math with currency. You probably either want to use integers (i.e., do all math in cents instead of dollars) or use exact precision numbers.
If you known nothing at all about the behavior of the objective function, there is no other way than trying all possible values.
On the opposite if you have a guarantee that the minimum is unique, the Golden section method will converge very quickly. This is a variant of the Fibonacci search, which is known to be optimal (require the minimum number of function evaluations).
Your function may have different properties which call for other algorithms.
Why not implementing binary search ?
<?php
$high_amount = 50;
// computed val2 is placed outside the loop
// no need te recalculate it each time
$val2 = find_out_2();
$previous_var = variance(find_out_1(0.01), $val2);
$start = 0;
$end = $high_amount * 100;
$closest_variance = NULL;
while ($start <= $end) {
$section = intval(($start + $end)/2);
$cursor = $section / 100;
$val1 = find_out_1($cursor);
$variance = variance($val1, $val2);
if ($variance <= $previous_var) {
$start = $section;
}
else {
$closest_variance = $cursor;
$end = $section;
}
}
if (!is_null($closest_variance)) {
$closest_variance -= 0.01;
}

How to tell if a comma delimited list of numbers obeys the natural order of numbers

I have a comma delimited list of numbers which i am converting into an array and what i want to know about the list of numbers is if the numbers listed obey a natural ordering of numbers,you know,have a difference of exactly 1 between the next and the previous.
If its true the list obeys the natural ordering,i want to pick the first number of the list and if not the list obeys not the natural order,i pick the second.
This is my code.
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
$data = explode(',', $b);
$count = count($data);
foreach($data as $index => $number)
{
/**
If array has exactly one value
*/
if($count == 1){
echo 'number is:'.$number;
exit();
}
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
echo $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
echo $number_picked.'wrong';
}
}
?>
The problem i am having is to figure out how to test the difference for all array elements.
No loops are needed, a little bit of maths will help you here. Once you have your numbers in an array:
$a = explode(',', '5,7,8,9,10');
pass them to this function:-
function isSequential(array $sequence, $diff = 1)
{
return $sequence[count($sequence) - 1] === $sequence[0] + ($diff * (count($sequence) - 1));
}
The function will return true if the numbers in the array follow a natural sequence. You should even be able to adjust it for different spacings between numbers, eg 2, 4, 6, 8, etc using the $diff parameter, although I haven't tested that thoroughly.
See it working.
Keep in mind that this will only work if your list of numbers is ordered from smallest to largest.
Try using a function to solve this... Like so:
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
function test($string) {
$data = explode(',', $string);
if(count($data) === 1){
return 'number is:'.$number;
}
foreach($data as $index => $number)
{
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
return $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
return $number_picked.'wrong';
}
}
}
echo test($a);
echo test($b);
echo test($c);
?>
You already know how to explode the list, so I'll skip that.
You already handle a single item, so I'll skip that as well.
What is left, is checking the rest of the array. Basically; there's two possible outcome values: either the first element or the second. So we'll save those two first:
$outcome1 = $list[0];
$outcome2 = $list[1];
Next, we'll loop over the items. We'll remember the last found item, and make sure that the difference between the new and the old is 1. If it is, we continue. If it isn't, we abort and immediately return $outcome2.
If we reach the end of the list without aborting, it's naturally ordered, so we return $outcome1.
$lastNumber = null;
foreach( $items as $number ) {
if($lastNumber === null || $number - $lastNumber == 1 ) {
// continue scanning
$lastNumber = $number;
}
else {
// not ordened
return $outcome2;
}
}
return $outcome1; // scanned everything; was ordened.
(Note: code not tested)
To avoid the headache of accessing the previous or next element, and deciding whether it still is inside the array or not, use the fact that on a natural ordering the item i and the first item have a difference of i.
Also the corner case you call condition 3 is easier to handle outside the loop than inside of it. But easier still, the way we characterize a natural ordered list holds for a 1-item list :
$natural = true;
for($i=1; $i<$count && $natural; $i++)
$natural &= ($data[$i] == $data[0] + $i)
$number = $natural ? $data[0] : $data[1];
For $count == 1 the loop is never entered and thus $natural stays true : you select the first element.

What's wrong with my function for finding highest prime factor?

function find_highest_prime_factor($n)
{
for ($i = 2; $i <= $n; $i++)
{
if (bcmod($n, $i) == 0) //its a factor
{
return max($i, find_highest_prime_factor(bcdiv($n,$i)));
}
}
if ($i == $n)
{
return $n; //it's prime if it made it through that loop
}
}
UPDATE: This is the correct answer, my bad!
Get rid of the final if statement otherwise if $i!=sqrt($n) because sqrt($n) is not an integer you have an undefined return value
function find_highest_prime_factor($n){
for ($i = 2; $i <= sqrt($n); $i++) //sqrt(n) is the upperbound
{
if (bcmod($n, $i) == 0) //its a factor
{
return max($i, find_highest_prime_factor(bcdiv($n,$i)));
}
}
return $n; //it's prime if it made it through that loop
}
Line 11 should be:
if ($i == ceil(sqrt($n)))
Starting at 2 and stepping by 1 is inefficient. At least check 2 separately and then loop from 3 stepping 2 each time. Using a 2-4 wheel would be even faster.
When you recurse your code starts again at trial factor 2. It would be better to pass a second parameter holding the factor you have reached so far. Then the recursion wouldn't have go back over old factors that have already been tested.

Categories