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, ...).
Related
I have a mathematical question with PHP. Where n is a positive integer, this function f(n) satisfies the following.
This is a question asked in my programming class and now I am now trying to create a program to find f(n) using PHP, but now I am confused because this equation contains more than one function and I do not know how to put this in PHP. If you have any idea on how to put this equation into some codes, please post your idea. I would like to know how to write php code to solve this kind of mathematical equations.
If you look closely to the equation you will find this is Fibonacci Series.you can solve this using recursive function Like this.
function fib($n) {
if ($n < 0) {
return NULL;
} elseif ($n === 0) {
return 0;
} elseif ($n === 1 || $n === 2) {
return 1;
} else {
return fib($n-1) + fib($n-2);
}
}
As you can see i am calling same function until the base condition satisfied. Hope this help
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;
}
This question is born out of pure laziness and the desire to do more with less code.
Say I have a variable $x which needs to be greater than 0 and less than 12, what is the fastest (least amount of code written) way to check. Is there a faster way than this.
if($x < 0 || $x > 12) {
die("invalid x value");
}
It would be nice (and I think some languages have it) to do this:
if(0 > $x > 12) {
die("invalid x value");
}
Very curious to see if there is some PHP magic I am missing out on.
You can use filter_var as a native PHP function : http://de2.php.net/manual/en/function.filter-var.php But I don't find it any better, as you will need to pass an array with min and max range, which is not fast, nor short.
Maybe a user-defined function for this will fit? Yes, you will need to write the code once, but only once.
function between($value, $from, $to) {
if ($value < $from || $value > $to) {
return false;
}
return true;
}
The function return false, if the value is less than the min bound, or greater than the max bound. Otherwise it returns true. So if you need to stop you script, if the value is NOT between, you would need to ask for the false response if(!between...
So you only call it this way:
$x = 14;
if(!between($x, 0, 12)) {
die("invalid x value");
}
Output:
invalid x value
if, for example your $x is 5 and you want to check if it's between, and if it is - to continue the script, you ask for the true response.
if(between($x, 0, 12)
Basically, I'm trying to create a frame around text and on the third line where the text goes, it doesn't print out the # that's supposed to be the "padding" around it.
******************
*################*
*#HELLO, JUSTIN!*
*################*
******************
That's what it looks like - after the ! in the $greeting variable, there should be a #. The code is below, could anyone explain why this happens?
<html>
<body><?php
$pad = 1;
$rows = ($pad * 2) + 3;
$greeting = "HELLO, JUSTIN!";
$col = strlen($greeting) + (2 * $pad) + 2;
for ($r = 0; $r != $rows; ++$r)
{
for ($c = 0; $c != $col; ++$c)
{
if ($r == $pad + 1 && $c == $pad + 1)
{
echo $greeting;
$c += strlen($greeting);
//echo "#";
}
else
{
if ($r == 0 || $r == $rows - 1 || $c == 0 || $c == $col - 1)
echo "*";
else
echo "#";
}
}
echo "<br />";
}
?></body>
</html>
The vertical padding (above and below) the $greeting is easy to do, the trickiest part seems to be getting the row containing $greeting to display right.
Try using <= (less-than-or-equal-to) in your for-loop, instead of != (not-equal[s]).
You will have to change the ranges on your if statements.
$text = "justin rocks my socks!";
$asteriskAt = 0;
$poundAt = 1;
$padding_left = 2;
$padding_right = 2;
//table looks like
//ABCDEFG
//HIJKLMN
//OPQRSTU
//row view
//0000000
//1111111
//3333333
//column view
//0123456
//0123456
//0123456
// ^ all the same "table" (anything with x and y, or rows and cols), just different ways of viewing it.
$num_cols = strlen($text) + ($padding_left + $padding_right); //so far so good?
//strlen($text) evaluates to 22
//$num_cols evaluates to 26 ( = 22 + 2 + 2)
for ($c = 0; $c <= $num_cols; ++$c) {
if ($c == $asteriskAt || $c == $num_cols - $asteriskAt) {
echo '*';
}
if ($c == $poundAt || $c == $num_cols - $poundAt) {
echo '#';
}
if ($c > $poundAt || $c < $num_cols - $poundAt) {
// is it clear why I picked this "range" ?
// if you do $c > 2 || $c < $num_cols - 2
// you will display something different
// because the cols and rows are zero-indexed
//
//cols
// 0123456
// ^2 is actually slot #3
//our thing will look like
//01___56
//compared with
//0123456
//01___56 <- (padding dudes)
//0123456
$idx = $c - $padding_left;
echo $text[$idx];
}
}
By the way, www.ideone.com has a PHP interpreter, too! awesome.
Check out the differences in these 2 outputs, and notice that I only changed the one operator in the for-loop:
http://ideone.com/M1lpM //asterisk :D
http://ideone.com/JqsTx //no last asterisk =(
the code you posted is not bad, especially if it works on the first try!
but really, programming and software in general demands precision, and you can't get angry at a computer for doing what you tell it. Thus, it's much better to work in stages or steps, for when you could use clarity in debugging =)
Programmers learn gradually (if they are stubborn like me) that it's not only about having the code work 2 minutes from when you started writing it, it's about having it be readable and change-able, maintainable in the future. It's a good habit to develop, since you never know how long it will be before you look at your code again "what the heck was this crazy person doing?!" you might think, then realize you wrote it a year ago ...and you never know who will end up maintaining whatever you make, whether you contribute to an open source project or work on a commercial application.
So, some suggestions (that I would tell my 6-years-ago-self, really) :
if (X || Y || Z || Q || P || NINE || WOLFHOUNDS || CATS || LION) {
//this will be incredibly hard to debug, so try to avoid it
//because it's basically a gateway, a CHANNEL into pandora's box
//and pandora's heart and soul
//and it's not fun in pandora's part of the universe
//although she is just trying to be happy like everyone else
//and we should not really discredit her opinions
//but man is this if statement going to be bothersome
//if one little thing goes wrong
//and we gotta figure out what caused it
}
If you have any questions about the code I posted, don't hesitate to ask =)
hopefully this helps you figure out why the code you posted doesn't work the way you expected it to, but you can very easily make a Gordian Knot that is very difficult to cut through without perfect wisdom / a life of meditation and moderation
$c += strlen($greeting);
must be replaced with
$c += strlen($greeting)-1;
You forgot to accommodate for the padding:
Change
$c += strlen($greeting);
to
$c += strlen($greeting) - $pad;
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.