OK, I've had the same problem for a few weeks now and cant perfect it.
Aim
To build a regular deposit savings account system where it prints out the total balance at the current time.
Problem
The current equation I have:
If the interest is 6% with the user paying in 200 a month with compound being each month the balance would be after 6 months 1,220.61
I am getting 1217.13
I have tested different lengths of time and many different online calculators, my calculation is always less.
My code
<h2>Total Balance To Date</h2>
<?php
$p = 0; // Starting amount
$i = 0.06; // Interest rate
$c = 12; // compound frequency set to monthly
$n = 6/12; // Current time invested set to 6 months
$r = 200; // Monthly investment is 200
$x = $i / $c;
$y = pow((1 + $x), ($n * $c));
if($p!=0)
{
$vf = $p * $y + ($r * ($y - 1) / $x);
}
else
{
$vf = 1 + $y + ($r * ($y - 1) / $x);
}
?>
<p>£<?php echo round($vf, 2, PHP_ROUND_HALF_UP); ?></p> // Comes out at 1217.13
LINK to sandbox https://3v4l.org/9X7OH
Setting
q = pow(1.06 , 1.0/12) = 1.0048675505653430
and computing
200*(q+q^2+q^3+q^4+q^5+q^6) = 200*q*(q^6-1)/(q-1)
gives the result
1220.61037336530790
which is obviously what the online calculators worked with. It is slightly wrong, as for the nominal interest rate, the monthly compound factor should be
q = 1 + 0.06/12 = 1.005
resulting in a balance after 6 months of
1221.1758776293781
As you see, you got the formula almost right, it should be
$vf = $p * $y + ($r * (1 + $x) * ($y - 1) / $x);
since the rate is deposited at the start of the month, so the first rate gets compounded as r*(1+x)^6 and the last rate as r*(1+x). However, the second formula in the else branch does not make sense at all.
Related
i'm trying to increase a variable value depending on the other variable value for example:
i have a variable called $totalhousesleft...
i want to set a price depending on how many $totalhousesleft i have...
everytime the totalhousesleft is down by 10, i want to increase the variable $currentprice by 1.
the starting value of $totalhouses left is 8000 and every time it goes down by 10, i set the $currentprice +1... the starting value of current price is 9...
something like:
If ($totalhousesleft >= 8000) {$currentprice = 9; $sellingprice = 8;}
If ($totalhousesleft >= 7990) {$currentprice = 10; $sellingprice = 9;}
If ($totalhousesleft >= 7980) {$currentprice = 11; $sellingprice = 10;}
If ($totalhousesleft >= 7970) {$currentprice = 12; $sellingprice = 11;}
ALL THE WAY DOWN UNTIL HOUSES LEFT IS 1. If someone can please show me a loop or a shorter code i would really appreciate it!
#elias-soares answer is close, but is missing ceil...and an explanation.
foreach ( [8000, 7995, 7990, 7985, 7980, 7975, 7970, 7965] as $totalhousesleft ) {
$currentprice = 9 + ((ceil(800 - ((min(8000, $totalhousesleft)) / 10))) * 1);
$sellingprice = $currentprice - 1;
}
Try it here: https://onlinephp.io/c/68196
Let's break down how to get $currentprice:
//$currentprice = ceil(9 + (800 - (min(8000, $totalhousesleft) / 10)));
// get the lesser of 8000, or $totalhousesleft
// in other words, 8000 is the maximum number to calculate
$totalhousesleft = min(8000, $totalhousesleft);
// divide total houses left into number of tenth units
$tenth = $totalhousesleft / 10;
// since the price increases when the number of tenth units decreases,
// the unit factor is the difference between the max possible tenths
// and tenths of the current total houses left
$tenthunit = 800 - $tenth;
// tenth unit is fractional for values not evenly divisible by 10,
// so round up
$tenthroundup = ceil($tenthunit);
// multiply the number of tenth units with the price per unit
$pricepertenth = $tenthroundup * 1; // 1 currency per tenth unit
// add the price per tenth cost to the base cost (9 currency)
$currentprice = 9 + $pricepertenth;
Bonus: this can be implemented in a function:
function getPrices ($totalhousesleft, $baseprice = 9, $discount = 1, $priceperunit = 1, $maxtotal = 8000, $units = 10) {
$currentprice = $baseprice + ((ceil(($maxtotal / $units) - ((min($maxtotal, $totalhousesleft)) / $units))) * $priceperunit);
return [$currentprice, $currentprice - $discount];
}
foreach ( [8000, 7995, 7990, 7985, 7980, 7975, 7970, 7965] as $totalhousesleft ) {
list($currentprice, $sellingprice) = getPrices($totalhousesleft);
}
Try it here: https://onlinephp.io/c/2672b
A for or while loop could be used for this. I'd use for:
$iteration = 0;
for($x = 8000; $x > 0; $x = $x - 10){
if(empty($iteration)) {
$iteration = $x/1000;
}
if ($totalhousesleft >= $x) {
$currentprice = $iteration;
$sellingprice = $currentprice + 1;
break;
}
$iteration++;
}
if(empty($currentprice)){
$currentprice = $iteration;
$sellingprice = $currentprice + 1;
}
This iterates over until a match is found then breaks out of the looping. The prices are based on the iteration it is in.
Demo link: https://3v4l.org/Mm432 (updated for edge cases 0-9)
You can use Math.
$currentprice = 9 + (800 - (min(8000,$totalhousesleft)/10));
$sellingprice = $currentprice - 1;
Imagine a scenario where monthly rent is 100,000 which is payable at the end of each month.
A tenant then decides to make a payment 350,000 to cater for the current month and those ahead. How do I distribute this amount since I can obviously see here that this amount caters for 3 months and a half month?
Here is what I was trying in PHP but I just can't get the last 50,000 to appear.
$rent = 100000; // rent amount
$amountPaid = 350000; // amount paid by tenant
$length = $amountPaid/$rent; // number of months paid for
for ($c = 1; $c <= $length; $c++)
{
$foreachMonth = $rent;
assignRentFunction($c, $foreachMonth);
}
function assignRentFunction($count, $amt)
{
echo "Month ".$count.': '.$amt."<br>";
}
Steps:
1) Get total months with ceil() function.
2) It will return 4 months. 3 months fully paid and one month paid only 50000.
3) Now, for each loop will add 10000 to total rent paid.
4) if this surpasses the amount paid, get mod which is 50000
$rent = 100000; // rent amount
$amountPaid = 350000; // amount paid by tenant
$length = ceil($amountPaid/$rent); // number of months paid for
$totalRent = 0;
for ($c = 1; $c <= $length; $c++) {
$totalRent += $rent;
$foreachMonth = $rent;
if ($amountPaid < $totalRent) { // Here is the logic, if amount exceeds, use the remaining amount.
$foreachMonth = $amountPaid % $rent;
}
assignRentFunction($c, $foreachMonth);
}
function assignRentFunction($count, $amt) {
echo "Month ".$count.': '.$amt."<br>";
}
**Output:**
Month 1: 100000
Month 2: 100000
Month 3: 100000
Month 4: 50000
$rent= 100000; // rent amount
$amountPaid= 350000; // amount paid by tenant
$length= $amountPaid/$rent; // number of months paid for
for ($c = 1; $c <= ceil($length); $c++)
{
$foreachMonth = 100000;
if($amountPaid>$rent)
{
$rent=$rent;
$amountPaid=$amountPaid-$rent;
}
else
{
$rent=$rent-$amountPaid;
}
assignRentFunction($c, $rent);
}
function assignRentFunction($count, $amt)
{
echo "Month ".$count.': '.$amt."<br>";
}
Since there seems to be a few ways to slice this, I thought I'd throw my hat in the ring also:
for($c = 1; $c<=ceil($amountPaid/$rent); $c++){
assignRentFunction($c, $rent - max(($c * $rent - $amountPaid),0));
}
And now the commented version:
for($month = 1; $month<=ceil($amountPaid/$rent); $month++){
//For each month there is money for rent (using ceil() to account for fractions)
assignRentFunction(
$month,
// The number of the month
$rent
//Show the rent ($rent)
-
//Deduct
max(($month * $rent - $amountPaid),0)
//Any difference if the whole rent for that month hasn't been paid
/**
* This relies on a little hack with the max() function:
* max($var,0) will return 0 if $var is less than 0.
* So we check to see if the sum of rent up to that month ($month * $rent)
* is greater than what was paid ($month * $rent) - $amountPaid.
* If it isn't because it's wrapped in the max,
* the net (negative) number will just be shown as nill.
* If it is, the net positive number will be subtracted
* from the month's rent.
**/
);
}
Change your loop part to
for ($c = 1; $c <= $length; $c++) {
$foreachMonth = $rent;
assignRentFunction($c, $foreachMonth);
}
$fractionalMonth = $length - intval($length);
if ($fractionalMonth)
assignRentFunction($c, $foreachMonth * $fractionalMonth);
Your error was since you're always incrementing $c by one, you wasn't able to get the fractional part for the last month
You can solve your problem like this,
$rent = 100000; // rent amount
$amountPaid = 350000; // amount paid by tenant
$length = ceil($amountPaid / $rent); // number of months paid for
$temp1 = $amountPaid;
for ($c = 1; $c <= $length; $c++) {
if($temp1 < $rent){ // assigning left amount to rent if less than amount left
$rent = $temp1;
}
if($temp1 > $rent){ // checking if amount left is still more than per month rent then minus that rent from pending paid amount
$temp1 = $temp1 - $rent;
}
assignRentFunction($c, $rent);
}
function assignRentFunction($count, $amt)
{
echo "Month " . $count . ': ' . $amt . "<br>";
}
Demo.
I wrote a simple code to calculate a math equation, but I would like to iterate through numbers from $start to $end. I am making a game, and this page will calculate the amount of experience it takes to reach the next level and insert it into the database. What would be the best way to iterate from $start to $end and calculate the amount of exp needed for that level?
Code:
<?php
$start = 1;
$end = 100;
$level = $start++;
$l = $level - 1;
$exp = ((40*($l * $l)) + (360 * $l));
?>
As it sits right now it calculates the first level but i cannot for the life of me figure out how to make it go through til it reaches $end.
$exp = 0;
for($level = $start; $level <= $end; $level++){
$exp += 40 * $l ** 2 + 360 * $l;
}
Actually, we can use mathematics to make this faster by generalizing the experience level required. Since your experience function is the summation of a quadratic function:
f(n)
= S[1 100] 40n^2 + 360n
= 40n (n + 1) (2n + 1) / 6 + 360n (n + 1) / 2
In PHP:
40 * $level * ($level + 1) * (2 * $level + 1) / 6 + 360 * $level * ($level + 1) / 2
Or simplify it further if you like.
This is definitely faster than calculating a loop 100 times.
If $start is not 1, simply use f(end) - f(start - 1).
You have to calculate the needed xp for every single level, so you should put the calculation code inside a loop that starts at your lowest level and proceeds until it hits the top limit / end level. You can choose from two different loop types in PHP, the for-loop and the while-loop.
Personally, I would choose the while-loop for this specific "problem", but that is a thing everyone has to decide on his own. The code for your calculator would look like this:
// Create an extra variable to store the level for which you are currently calculating the needed xp
$i = $start;
// The while-loop (do this code until the current level hits the max level as specified)
while($i <= end) {
// use $i to calculate your exp, its the current level
// Insert code here...
// then add 1 to $i and do the same again (repeat the code inside loop)
$i++;
}
Here are some links to the documentation of php:
while-loop
for-loop
OK, using the answers over at this question I was able to produce the following code to calculated the experience needed to level on a text-based RPG I am working on. I'll say from the get-go that my math skills are not great, so please "dumb it down" as much as you can.
function calcExp($L) {
if($L <= 5) {
//If Level 5 or less then just 12 exp per level
return 12*$L;
}
if($L > 1000) {
//If over level 1,000, we need to slow them down
//add an additional 500k per level required
$calc = $L - 1000;
$c2 = 500000*$calc;
return (5*$L*$L-5*$L)+$c2;
}
else {
//otherwise, calculate as follows
$exp = 5 * $L * $L - 5 * $L;
return $exp;
}
}
That returns the correct amount of experience, now I'm trying to figure out how to reverse the process so that I can checkLvl($EXPERIENCE) and have it return the level it should be at.
So then if I were to
$level = 10;
$exp = 450;
$check = checkLvl($exp);
if($check != $level) { die('Unknown Error'); }
else { echo "Success!"; }
Note: the above code is just me trying to be clear.
I'm at a loss as to how to reverse the little mess I created.
Your first five levels are only tweleve exp per level, so you could reverse and say the first 60 exp (5 * 12) should be floor($exp / 12)
Your second level range is from 6 to 1000. So if the exp is greater than 60, but less than whatever exp you need for level 1000, you'd solve $exp = 5 * $L * $L - 5 * $L for $L, which is a quadratic equation. It would look something like $L = (5 + sqrt(25 - 4 * 5 * -$exp)) / (2 * 5)
Everything else above level 1000 should solve a similar quadratic equation, $exp = (5 * $L * $L - 5 * $L) + 500000 * ($L - 1000) for $L. It would look like $L = ((5 + 500000) + sqrt((5 + 500000)^2 - 4 * 5 * (500000 - $exp))) / (2 * 5)
Writing a routine to display data on a horizontal axis (using PHP gd2, but that's not the point here).
The axis starts at $min to $max and displays a diamond at $result, such an image will be around 300px wide and 30px high, like this:
(source: testwolke.de)
In the example above, $min=0, $max=3, $result=0.6.
Now, I need to calculate a scale and labels that make sense, in the above example e.g. dotted lines at 0 .25 .50 .75 1 1.25 ... up to 3, with number-labels at 0 1 2 3.
If $min=-200 and $max=600, dotted lines should be at -200 -150 -100 -50 0 50 100 ... up to 600, with number-labels at -200 -100 0 100 ... up to 600.
With $min=.02and $max=5.80, dotted lines at .02 .5 1 1.5 2 2.5 ... 5.5 5.8 and numbers at .02 1 2 3 4 5 5.8.
I tried explicitly telling the function where to put dotted lines and numbers by arrays, but hey, it's the computer who's supposed to work, not me, right?!
So, how to calculate???
An algorithm (example values $min=-186 and $max=+153 as limits):
Take these two limits $min, $max and mark them if you wish
Calculate the difference between $max and $min: $diff = $max - $min
153 - (-186) = 339
Calculate 10th logarithm of the difference $base10 = log($diff,10) = 2,5302
Round down: $power = round($base10) = 2.
This is your tenth power as base unit
To calculate $step calculate this:
$base_unit = 10^$power = 100;
$step = $base_unit / 2; (if you want 2 ticks per one $base_unit).
Calculate if $min is divisible by $step, if not take the nearest (round up) one
(in the case of $step = 50 it is $loop_start = -150)
for ($i=$loop_start; $i<=$max; $i++=$step){ // $i's are your ticks
end
I tested it in Excel and it gives quite nice results, you may want to increase its functionality,
for example (in point 5) by calculating $step first from $diff,
say $step = $diff / 4 and round $step in such way that $base_unit is divisible by $step;
this will avoid such situations that you have between (101;201) four ticks with $step=25 and you have 39 steps $step=25 between 0 and 999.
ACM Algorithm 463 provides three simple functions to produce good axis scales with outputs xminp, xmaxp and dist for the minimum and maximum values on the scale and the distance between tick marks on the scale, given a request for n intervals that include the data points xmin and xmax:
Scale1() gives a linear scale with approximately n intervals and dist being an integer power of 10 times 1, 2 or 5.
Scale2() gives a linear scale with exactly n intervals (the gap between xminp and xmaxp tends to be larger than the gap produced by Scale1()).
Scale3() gives a logarithmic scale.
The original 1973 paper is online here, which provides more explanation than the code linked to above.
The code is in Fortran but it is just a set of arithmetical calculations so it is very straightforward to interpret and convert into other languages. I haven't written any PHP myself, but it looks a lot like C so you might want to start by running the code through f2c which should give you something close to runnable in PHP.
There are more complicated functions that give prettier scales (e.g. the ones in gnuplot), but Scale1() would likely do the job for you with minimal code.
(This answer builds on my answer to a previous question Graph axis calibration in C++)
(EDIT -- I've found an implementation of Scale1() that I did in Perl):
use strict;
sub scale1 ($$$) {
# from TOMS 463
# returns a suitable scale ($xMinp, $xMaxp, $dist), when called with
# the minimum and maximum x values, and an approximate number of intervals
# to divide into. $dist is the size of each interval that results.
# #vInt is an array of acceptable values for $dist.
# #sqr is an array of geometric means of adjacent values of #vInt, which
# is used as break points to determine which #vInt value to use.
#
my ($xMin, $xMax, $n) = #_;
#vInt = {1, 2, 5, 10};
#sqr = {1.414214, 3.162278, 7.071068 }
if ($xMin > $xMax) {
my ($tmp) = $xMin;
$xMin = $xMax;
$xMax = $tmp;
}
my ($del) = 0.0002; # accounts for computer round-off
my ($fn) = $n;
# find approximate interval size $a
my ($a) = ($xMax - $xMin) / $fn;
my ($al) = log10($a);
my ($nal) = int($al);
if ($a < 1) {
$nal = $nal - 1;
}
# $a is scaled into a variable named $b, between 1 and 10
my ($b) = $a / 10^$nal;
# the closest permissable value for $b is found)
my ($i);
for ($i = 0; $i < $_sqr; $i++) {
if ($b < $sqr[$i]) last;
}
# the interval size is computed
$dist = $vInt[$i] * 10^$nal;
$fm1 = $xMin / $dist;
$m1 = int($fm1);
if ($fm1 < 0) $m1--;
if (abs(($m1 + 1.0) - $fm1) < $del) $m1++;
# the new minimum and maximum limits are found
$xMinp = $dist * $m1;
$fm2 = $xMax / $dist;
$m2 = $fm2 + 1;
if ($fm2 < -1) $m2--;
if (abs ($fm2 + 1 - $m2) < $del) $m2--;
$xMaxp = $dist * $m2;
# adjust limits to account for round-off if necessary
if ($xMinp > $xMin) $xMinp = $xMin;
if ($xMaxp < $xMax) $xMaxp = $xMax;
return ($xMinp, $xMaxp, $dist);
}
sub scale1_Test {
$par = (-3.1, 11.1, 5,
5.2, 10.1, 5,
-12000, -100, 9);
print "xMin\txMax\tn\txMinp\txMaxp,dist\n";
for ($i = 0; $i < $_par/3; $i++) {
($xMinp, $xMaxp, $dist) = scale1($par[3*$i+0],
$par[3*$i+1], $par[3*$i+2]);
print "$par[3*$i+0]\t$par[3*$i+1]\t$par[3*$i+2]\t$xMinp\t$xMaxp,$dist\n";
}
}
I know that this isn't exactly what you are looking for, but hopefully it will get you started in the right direction.
$min = -200;
$max = 600;
$difference = $max - $min;
$labels = 10;
$picture_width = 300;
/* Get units per label */
$difference_between = $difference / ($labels - 1);
$width_between = $picture_width / $labels;
/* Make the label array */
$label_arr = array();
$label_arr[] = array('label' => $min, 'x_pos' => 0);
/* Loop through the number of labels */
for($i = 1, $l = $labels; $i < $l; $i++) {
$label = $min + ($difference_between * $i);
$label_arr[] = array('label' => $label, 'x_pos' => $width_between * $i);
}
A quick example would be something in the lines of $increment = ($max-$min)/$scale where you can tweak scale to be the variable by which the increment scales. Since you devide by it, it should change proportionately as your max and min values change. After that you will have a function like:
$end = false;
while($end==false){
$breakpoint = $last_value + $increment; // that's your current breakpoint
if($breakpoint > $max){
$end = true;
}
}
At least thats the concept... Let me know if you have troubles with it.