Can someone explain this php function to me? - php

I'm trying to understand everything this PHP function does. A game has this code and I'm trying to determine how much XP it takes to level for each level.
function xpToNextLevel($level){
if($level == 0){
return 200;
}
$s = 500;
$m = 0xFFFF * ($level / 98);
$xp = floor((($m - $s) * ($level / 98)) + $s);
return $xp;
}
Thanks!

I think it's great to look over other people's code (open-source or with permission that is) to better understand the language. While I understand the down-voting, as google would have been a good resource to figure this out, I'll try to explain it the best I can.
To help explain it, I have rewritten a bit of your code for you.
function xpToNextLevel($level){
if($level == 0){
return 200;
}
$d = $level / 98;
$s = 500;
$m = 65535 * $d; /* 65535 is equivalent to 0xFFFF */
$k = $m - $s; /* takes (0xFFFF * ($level / 98)) - $s */
$dk = $k * $d; /* takes ($m - $s) * ($level/98) */
$unroundedXP = $dk + $s; /* adds (($m-$s) * ($level/98)) to $s */
$xp = floor($unroundedXP); /* rounds the experience down. */
return $xp;
}
so, I know in your comment you asked about the level 4. Let's trace through that!
4 != 0 // thus we continue.
4/98 = 2/49 /*which is roughly ~0.04082, but for the sake of accuracy, I'll leave it as 2/49 */
65535 * (2/49) = ~2674.89795
2674.89795 - 500 = 2174.89795
2174.89795 * 2/49 = ~88.771345
88.771344 + 500 = 588.771344 //This is the unrounded experience
Experience = 588
The floor function basically rounds to the lowest integer. In positive numbers, this means we take the number and toss out everything behind the decimal point. So the number returned is roughly 588. I say roughly because a computer would be more exact about it.
By the way, I believe this is most comparable to a "Arithmetic Series." More information about that can be found here: http://mathworld.wolfram.com/ArithmeticSeries.html

So, let's simplify the formula mathematically. Following formulas are not a PHP code, but pure mathematics!
xp = ((65535 * (level / 98)) - 500) * (level / 98) + 500
... which simplifies to ...
xp = (65535 / 98^2) * level^2 - (500 / 98) * level + 500
... which is approximately equivalent to ...
xp = 6.8 * level^2 - 5.1 * level + 500
As You can see, the amount of experience needed for next level grows quadratically along the formula above.

Related

Struggling with maths... How to reverse calculate a percentage?

I believe this is a language agnostic question and more focused on math, however I prefer PHP. I know how to calculate percentages the normal (forward) way:
$percent = 45.85;
$x = 2000000;
$deduction = ($percent / 100) * $x; // 917,000
$result = $x - $deduction; // 1,083,000
What I would like to do, is be able to reverse the calculation (assuming I only know the $percent and $result), for example...
54.15% of x = 1,083,000
How do I calculate x? I know the answer is 2,000,000, but how do I program it to arrive at that answer?
I found a similar question & solution through Google but I just don't understand how to implement it...
You can do
1,083,000 * 100 / 54.15
In PHP, it will be
$x = $result * 100 / $percent
When you say 54.15% of x = 1083000, you mean 0.5415 * x = 1083000. To solve for x, divide 0.5415 from both sides: x = 1083000 / 0.5415. The PHP is:
$p = 54.15;
$r = 108300;
// First, make p a number, not a percent
$p = $p/100; // I would actually use $p/= 100;
// Now, solve for x
$x = $r/$p;

PHP algorithm to solve a system of linear equations of grade 1

I have a system of equations of grade 1 to resolve in PHP.
There are more equations than variables but there aren't less equations than variables.
The system would look like bellow. n equations, m variables, variables are x[i] where 'i' takes values from 1 to m. The system may have a solution or not.
m may be maximum 100 and n maximum ~5000 (thousands).
I will have to resolve like a few thousands of these systems of equations. Speed may be a problem but I'm looking for an algorithm written in PHP for now.
a[1][1] * x[1] + a[1][2] * x[2] + ... + a[1][m] * x[m] = number 1
a[2][1] * x[1] + a[2][2] * x[2] + ... + a[2][m] * x[m] = number 2
...
a[n][1] * x[1] + a[n][2] * x[2] + ... + a[n][m] * x[m] = number n
There is Cramer Rule which may do it. I could make 1 square matrix of coefficients, resolve the system with Cramer Rule (by calculating matrices' determinants) and than I should check the values in the unused equations.
I believe I could try Cramer by myself but I'm looking for a better solution.
This is a problem of Computational Science,
http://en.wikipedia.org/wiki/Computational_science#Numerical_simulations
I know there are some complex algorithms to solve my problem but I can't tell which one would do it and which is the best for my case. An algorithm would use me better than just the theory with the demonstration.
My question is, does anybody know a class, script, code of some sort written in PHP to resolve a system of linear equations of grade 1 ?
Alternatively I could try an API or a Web Service, best to be free, a paid one would do it too.
Thank you
I needed exactly this, but I couldn't find determinant function, so I made one myself. And the Cramer rule function too. Maybe it'll help someone.
/**
* $matrix must be 2-dimensional n x n array in following format
* $matrix = array(array(1,2,3),array(1,2,3),array(1,2,3))
*/
function determinant($matrix = array()) {
// dimension control - n x n
foreach ($matrix as $row) {
if (sizeof($matrix) != sizeof($row)) {
return false;
}
}
// count 1x1 and 2x2 manually - rest by recursive function
$dimension = sizeof($matrix);
if ($dimension == 1) {
return $matrix[0][0];
}
if ($dimension == 2) {
return ($matrix[0][0] * $matrix[1][1] - $matrix[0][1] * $matrix[1][0]);
}
// cycles for submatrixes calculations
$sum = 0;
for ($i = 0; $i < $dimension; $i++) {
// for each "$i", you will create a smaller matrix based on the original matrix
// by removing the first row and the "i"th column.
$smallMatrix = array();
for ($j = 0; $j < $dimension - 1; $j++) {
$smallMatrix[$j] = array();
for ($k = 0; $k < $dimension; $k++) {
if ($k < $i) $smallMatrix[$j][$k] = $matrix[$j + 1][$k];
if ($k > $i) $smallMatrix[$j][$k - 1] = $matrix[$j + 1][$k];
}
}
// after creating the smaller matrix, multiply the "i"th element in the first
// row by the determinant of the smaller matrix.
// odd position is plus, even is minus - the index from 0 so it's oppositely
if ($i % 2 == 0){
$sum += $matrix[0][$i] * determinant($smallMatrix);
} else {
$sum -= $matrix[0][$i] * determinant($smallMatrix);
}
}
return $sum;
}
/**
* left side of equations - parameters:
* $leftMatrix must be 2-dimensional n x n array in following format
* $leftMatrix = array(array(1,2,3),array(1,2,3),array(1,2,3))
* right side of equations - results:
* $rightMatrix must be in format
* $rightMatrix = array(1,2,3);
*/
function equationSystem($leftMatrix = array(), $rightMatrix = array()) {
// matrixes and dimension check
if (!is_array($leftMatrix) || !is_array($rightMatrix)) {
return false;
}
if (sizeof($leftMatrix) != sizeof($rightMatrix)) {
return false;
}
$M = determinant($leftMatrix);
if (!$M) {
return false;
}
$x = array();
foreach ($rightMatrix as $rk => $rv) {
$xMatrix = $leftMatrix;
foreach ($rightMatrix as $rMk => $rMv) {
$xMatrix[$rMk][$rk] = $rMv;
}
$x[$rk] = determinant($xMatrix) / $M;
}
return $x;
}
Wikipedia should have pseudocode for reducing the matrix representing your equations to reduced row echelon form. Once the matrix is in that form, you can walk through the rows to find a solution.
There's an unmaintained PEAR package which may save you the effort of writing the code.
Another question is whether you are looking mostly at "wide" systems (more variables than equations, which usually have many possible solutions) or "narrow" systems (more equations than variables, which usually have no solutions), since the best strategy depends on which case you are in — and narrow systems may benefit from using a linear regression technique such as least squares instead.
This package uses Gaussian Elimination. I found that it executes fast for larger matrices (i.e. more variables/equations).
There is a truly excellent package based on JAMA here: http://www.phpmath.com/build02/JAMA/docs/index.php
I've used it for simple linear right the way to highly complex Multiple Linear Regression (writing my own Backwards Stepwise MLR functions on top of that). Very comprehensive and will hopefully do what you need.
Speed could be considered an issue, for sure. But works a treat and matched SPSS when I cross referenced results on the BSMLR calculations.

Need to get multiplier that will bring fractions of a cent to a whole cent

I want to be able to specify a fraction of a cent (100th max) in a php application. I need to figure out how many iterations it would take to reach an even whole cent. I inherited an application that was supposedly doing this, but it's not working at all. Is there any direction someone can point me in? I apologize if this is beyond simple. I am just blanking out. In our app, it was doing something like this:
$integerPayout = (int)floor($payout * 10000);
$count = 1;
$modAmount = 100;
while(($integerPayout * $count) % $modAmount != 0)
$count += 1;
echo $count;
lcm(100, (int)($payout * 100)) / (int)($payout * 100)

Bell Curve Algorithm With PHP

I am working on a personal project in which IQ ranges will be randomly assignes to fake characters. This asignment will be random, yet realistic, so IQ ranges must be distributed along a bell curve. There are 3 range categories: low, normal, and high. The half of the fake characters will fall within normal, but about 25% will either fall into the low or high range.
How can I code this?
It might look long and complicated (and was written procedural for PHP4) but I used to use the following for generating non-linear random distributions:
function random_0_1()
{
// returns random number using mt_rand() with a flat distribution from 0 to 1 inclusive
//
return (float) mt_rand() / (float) mt_getrandmax() ;
}
function random_PN()
{
// returns random number using mt_rand() with a flat distribution from -1 to 1 inclusive
//
return (2.0 * random_0_1()) - 1.0 ;
}
function gauss()
{
static $useExists = false ;
static $useValue ;
if ($useExists) {
// Use value from a previous call to this function
//
$useExists = false ;
return $useValue ;
} else {
// Polar form of the Box-Muller transformation
//
$w = 2.0 ;
while (($w >= 1.0) || ($w == 0.0)) {
$x = random_PN() ;
$y = random_PN() ;
$w = ($x * $x) + ($y * $y) ;
}
$w = sqrt((-2.0 * log($w)) / $w) ;
// Set value for next call to this function
//
$useValue = $y * $w ;
$useExists = true ;
return $x * $w ;
}
}
function gauss_ms( $mean,
$stddev )
{
// Adjust our gaussian random to fit the mean and standard deviation
// The division by 4 is an arbitrary value to help fit the distribution
// within our required range, and gives a best fit for $stddev = 1.0
//
return gauss() * ($stddev/4) + $mean;
}
function gaussianWeightedRnd( $LowValue,
$maxRand,
$mean=0.0,
$stddev=2.0 )
{
// Adjust a gaussian random value to fit within our specified range
// by 'trimming' the extreme values as the distribution curve
// approaches +/- infinity
$rand_val = $LowValue + $maxRand ;
while (($rand_val < $LowValue) || ($rand_val >= ($LowValue + $maxRand))) {
$rand_val = floor(gauss_ms($mean,$stddev) * $maxRand) + $LowValue ;
$rand_val = ($rand_val + $maxRand) / 2 ;
}
return $rand_val ;
}
function bellWeightedRnd( $LowValue,
$maxRand )
{
return gaussianWeightedRnd( $LowValue, $maxRand, 0.0, 1.0 ) ;
}
For the simple bell distribution, just call bellWeightedRnd() with the min and max values; for a more sophisticated distribution, gaussianWeightedRnd() allows you to specify the mean and stdev for your distribution as well.
The gaussian bell curve is well suited to IQ distribution, although I also have similar routines for alternative distribution curves such as poisson, gamma, logarithmic, &c.
first assume you have 3 function to provide high medium and low IQs, then simply
function randomIQ(){
$dice = rand(1,100);
if($dice <= 25) $iq = low_iq();
elseif($dice <= 75) $iq = medium_iq();
else $iq = high_iq();
return $iq;
}
You could randomize multiple 'dice', random number from each adding up to the highest point. This will generate a normal distribution (approximately).
Using the link that ithcy posted I created the following function:
function RandomIQ()
{
return round((rand(-1000,1000) + rand(-1000,1000) + rand(-1000,1000))/100,0) * 2 + 100;
}
It's a little messy but some quick checking gives it a mean of approximately 100 and a roughly Normal Distribution. It should fall in line with the information that I got from this site.

Algorithm to add Color in Bezier curves

I'm playing with GD library for a while and more particuraly with Bezier curves atm.
I used some existant class which I modified a little (seriously eval()...). I found out it was a generic algorithm used in and convert for GD.
Now I want to take it to another level: I want some colors.
No problem for line color but with fill color it's harder.
My question is:
Is there any existant algorithm for that? I mean mathematical algorithm or any language doing it already so that I could transfer it to PHP + GD?
EDIT2
So, I tried #MizardX solution with a harder curve :
1st position : 50 - 50
final position : 50 - 200
1st control point : 300 - 225
2nd control point : 300 - 25
Which should show this :
And gives this :
EDIT
I already read about #MizardX solution. Using imagefilledpolygon to make it works.
But it doesn't work as expected. See the image below to see the problem.
Top graph is what I expect (w/o the blackline for now, only the red part).
Coordinates used:
first point is 100 - 100
final point is 300 - 100
first control point is 100 - 0
final control point is 300 - 200
Bottom part is what I get with that kind of algorithm...
Convert the Bezier curve to a polyline/polygon, and fill that. If you evaluate the Bezier polynomial at close enough intervals (~1 pixel) it will be identical to an ideal Bezier curve.
I don't know how familiar you are with Bezier curves, but here is a crash course:
<?php
// Calculate the coordinate of the Bezier curve at $t = 0..1
function Bezier_eval($p1,$p2,$p3,$p4,$t) {
// lines between successive pairs of points (degree 1)
$q1 = array((1-$t) * $p1[0] + $t * $p2[0],(1-$t) * $p1[1] + $t * $p2[1]);
$q2 = array((1-$t) * $p2[0] + $t * $p3[0],(1-$t) * $p2[1] + $t * $p3[1]);
$q3 = array((1-$t) * $p3[0] + $t * $p4[0],(1-$t) * $p3[1] + $t * $p4[1]);
// curves between successive pairs of lines. (degree 2)
$r1 = array((1-$t) * $q1[0] + $t * $q2[0],(1-$t) * $q1[1] + $t * $q2[1]);
$r2 = array((1-$t) * $q2[0] + $t * $q3[0],(1-$t) * $q2[1] + $t * $q3[1]);
// final curve between the two 2-degree curves. (degree 3)
return array((1-$t) * $r1[0] + $t * $r2[0],(1-$t) * $r1[1] + $t * $r2[1]);
}
// Calculate the squared distance between two points
function Point_distance2($p1,$p2) {
$dx = $p2[0] - $p1[0];
$dy = $p2[1] - $p1[1];
return $dx * $dx + $dy * $dy;
}
// Convert the curve to a polyline
function Bezier_convert($p1,$p2,$p3,$p4,$tolerance) {
$t1 = 0.0;
$prev = $p1;
$t2 = 0.1;
$tol2 = $tolerance * $tolerance;
$result []= $prev[0];
$result []= $prev[1];
while ($t1 < 1.0) {
if ($t2 > 1.0) {
$t2 = 1.0;
}
$next = Bezier_eval($p1,$p2,$p3,$p4,$t2);
$dist = Point_distance2($prev,$next);
while ($dist > $tol2) {
// Halve the distance until small enough
$t2 = $t1 + ($t2 - $t1) * 0.5;
$next = Bezier_eval($p1,$p2,$p3,$p4,$t2);
$dist = Point_distance2($prev,$next);
}
// the image*polygon functions expect a flattened array of coordiantes
$result []= $next[0];
$result []= $next[1];
$t1 = $t2;
$prev = $next;
$t2 = $t1 + 0.1;
}
return $result;
}
// Draw a Bezier curve on an image
function Bezier_drawfilled($image,$p1,$p2,$p3,$p4,$color) {
$polygon = Bezier_convert($p1,$p2,$p3,$p4,1.0);
imagefilledpolygon($image,$polygon,count($polygon)/2,$color);
}
?>
Edit:
I forgot to test the routine. It is indeed as you said; It doesn't give a correct result. Now I have fixed two bugs:
I unintentionally re-used the variable names $p1 and $p2. I renamed them $prev and $next.
Wrong sign in the while-loop. Now it loops until the distance is small enough, instead of big enough.
I checked the algorithm for generating a Polygon ensuring a bounded distance between successive parameter-generated points, and seems to work well for all the curves I tested.
Code in Mathematica:
pts={{50,50},{300,225},{300,25},{50,200}};
f=BezierFunction[pts];
step=.1; (*initial step*)
While[ (*get the final step - Points no more than .01 appart*)
Max[
EuclideanDistance ###
Partition[Table[f[t],{t,0,1,step}],2,1]] > .01,
step=step/2]
(*plot it*)
Graphics#Polygon#Table[f[t],{t,0,1,step}]
.
.
The algorithm could be optimized (ie. generate less points) if you don't require the same parameter increment between points, meaning you can chose a parameter increment at each point that ensures a bounded distance to the next.
Random examples:
Generate a list of successive points which lie along the curve (p_list)).
You create a line between the two end points of the curve (l1).
Then you are going to find the normal of the line (n1). Using this normal find the distance between the two furthest points (p_max1, and p_max2) along this normal (d1). Divide this distance into n discrete units (delta).
Now shift l1 along n1 by delta, and solve for the points of intersection (start with brute force and check for a solution between all the line segments in p_list). You should be able to get two points of intersection for each shift of l1, excepting boundaries and self intersection where you may have only have a single point. Hopefully the quad routine can have two points of the quad be at the same location (a triangle) and fill without complaint otherwise you'll need triangles in this case.
Sorry I didn't provide pseudo code but the idea is pretty simple. It's just like taking the two end points and joining them with a ruler and then keeping that ruler parallel to the original line start at one end and with successive very close pencil marks fill in the whole figure. You'll see that when you create your little pencil mark (a fine rectangle) that the rectangle it highly unlikely to use the points on the curve. Even if you force it to use a point on one side of the curve it would be quite the coincidence for it to exactly match a point on the other side, for this reason it is better to just calculate new points. At the time of calculating new points it would probably be a good idea to regenerate the curves p_list in terms of these points so you can fill it more quickly (if the curve is to stay static of course otherwise it wouldn't make any sense).
This answer is very similar to #MizardX's, but uses a different method to find suitable points along the Bezier for a polygonal approximation.
function split_cubic($p, $t)
{
$a_x = $p[0] + ($t * ($p[2] - $p[0]));
$a_y = $p[1] + ($t * ($p[3] - $p[1]));
$b_x = $p[2] + ($t * ($p[4] - $p[2]));
$b_y = $p[3] + ($t * ($p[5] - $p[3]));
$c_x = $p[4] + ($t * ($p[6] - $p[4]));
$c_y = $p[5] + ($t * ($p[7] - $p[5]));
$d_x = $a_x + ($t * ($b_x - $a_x));
$d_y = $a_y + ($t * ($b_y - $a_y));
$e_x = $b_x + ($t * ($c_x - $b_x));
$e_y = $b_y + ($t * ($c_y - $b_y));
$f_x = $d_x + ($t * ($e_x - $d_x));
$f_y = $d_y + ($t * ($e_y - $d_y));
return array(
array($p[0], $p[1], $a_x, $a_y, $d_x, $d_y, $f_x, $f_y),
array($f_x, $f_y, $e_x, $e_y, $c_x, $c_y, $p[6], $p[7]));
}
$flatness_sq = 0.25; /* flatness = 0.5 */
function cubic_ok($p)
{
global $flatness_sq;
/* test is essentially:
* perpendicular distance of control points from line < flatness */
$a_x = $p[6] - $p[0]; $a_y = $p[7] - $p[1];
$b_x = $p[2] - $p[0]; $b_y = $p[3] - $p[1];
$c_x = $p[4] - $p[6]; $c_y = $p[5] - $p[7];
$a_cross_b = ($a_x * $b_y) - ($a_y * $b_x);
$a_cross_c = ($a_x * $c_y) - ($a_y * $c_x);
$d_sq = ($a_x * $a_x) + ($a_y * $a_y);
return max($a_cross_b * $a_cross_b, $a_cross_c * $a_cross_c) < ($flatness_sq * $d_sq);
}
$max_level = 8;
function subdivide_cubic($p, $level)
{
global $max_level;
if (($level == $max_level) || cubic_ok($p)) {
return array();
}
list($q, $r) = split_cubic($p, 0.5);
$v = subdivide_cubic($q, $level + 1);
$v[] = $r[0]; /* add a point where we split the cubic */
$v[] = $r[1];
$v = array_merge($v, subdivide_cubic($r, $level + 1));
return $v;
}
function get_cubic_points($p)
{
$v[] = $p[0];
$v[] = $p[1];
$v = array_merge($v, subdivide_cubic($p, 0));
$v[] = $p[6];
$v[] = $p[7];
return $v;
}
function imagefilledcubic($img, $p, $color)
{
$v = get_cubic_points($p);
imagefilledpolygon($img, $v, count($v) / 2, $color);
}
The basic idea is to recursively split the cubic in half until the bits we're left with are almost flat. Everywhere we split the cubic, we stick a polygon point.
split_cubic splits the cubic in two at parameter $t. cubic_ok is the "are we flat enough?" test. subdivide_cubic is the recursive function. Note that we stick a limit on the recursion depth to avoid nasty cases really screwing us up.
Your self-intersecting test case:
$img = imagecreatetruecolor(256, 256);
imagefilledcubic($img, array(
50.0, 50.0, /* first point */
300.0, 225.0, /* first control point */
300.0, 25.0, /* second control point */
50.0, 200.0), /* last point */
imagecolorallocate($img, 255, 255, 255));
imagepng($img, 'out.png');
imagedestroy($img);
Gives this output:
I can't figure out how to make PHP nicely anti-alias this; imageantialias($img, TRUE); didn't seem to work.

Categories