generating resources for each level of building - php

Is it possible to change this code into function without using a loop?
$start = 80;
for ($i = 1; $i <= 10; $i++) {
$start = $start * 1.5;
echo "level ".$i.": ".$start."<br>";
}
function generate($start, $level){
// some code
return $start;
}

For level 1 you have:
$start = $start * 1.5;
For level 2 $start is result from level 1, so:
$start = ($start * 1.5) * 1.5;
This same as
$start = $start * 1.5 * 1.5;
And can be simplified to
$start = $start * pow(1.5, $level);
In the end your function should look like:
function generate($start, $level){
return $start * pow(1.5, $level);
}

if you want to get the same result(include the level print to screen) you can use this code:
function generate2($start, $from,$to){
if($from==$to+1)
return 1.5;
$tmp=$start*1.5;
echo "level ". ($from).": ".$tmp."<br>";
return 1.5*generate2($tmp,$from+1,$to);
}
Or this:
<?php
define ("MAX_LEVEL",10) ;
function generate($start, $level)
{
if($limit==0)
return 1.5;
$tmp=$start*1.5;
echo "level ". (MAX_LEVEL-$level+1).": ".$tmp."<br>";
return 1.5*generate($tmp,$level-1);
}
Here some check code:
$start = 80;//<=================your code
for ($i = 1; $i <= 10; $i++) {
$start = $start * 1.5;
echo "level ".$i.": ".$start."<br>";
}
echo"---------------------------- <br>";
generate(80,10);//<====================my code
echo"---------------------------- <br>";
generate2(80,1,10);
?>
if you not need the prints you can use very simple function:
function generate($start, $level){
return $start * pow(1.5, $level);
}

Here you go, a solution without "visible" loop:
generate(80,10);
function generate($start, $level){
$i=1; // Just a var
$array = array_fill(0, $level, $start); // create an array with $level elements, with value $start
array_map(function($v)use(&$i){ // Loop through the array and use $i
echo "Level $i: ".(array_product(array($v, pow(1.5, $i++))))."<br>"; // Some basic math and output
}, $array);
}
Online demo
Note that you'll need PHP 5.3+ since this function is using an anonymous function

Or, if you need just to output $start:
function generate($start, $limit)
{
$start = $start * 1.5;
echo $start."<br>";
if($limit>1)
return(generate($start,$limit-1));
}
generate(80,10);
My question - how to properly echo $level, without third parameter (0, in this case which should be incremented, no decremented:))? :)
EDIT: I would like to know better solution which will do the same, with two args:
function generate2($start, $limit,$base)
{
$start = $start * 1.5;
echo "level ".$base.": ".$start."<br>";
if($base<$limit)
return(generate2($start,$limit,$base+1));
}
generate2(80,10,1);
And final edit:
function generate($start, $limit,$i=0)
{
$i++;
$start = $start * 1.5;
echo "level ".$i.": ".$start."<br>";
if($limit>1)
{
return(generate($start,$limit-1,$i));
}
}
generate(80,10);
as answer to my self. :) Please test it (before down votes:)), and let me know about issues... Oh, i see - OP wants just 1 result, LOL...
Question wasn't clear to me (and not just to me, it seems) :)

Related

Php function call from another function

I am trying to make a text game in PHP but i have problem since i am programing in php for few days. I need to call function attack (I need $_mainAttack since it is combination of $_baseAttack and special attack) from defend function so i can calculate the health loss I have placed -5 just to see if it is working..
Also in health i need attack to be able to lower hp so i could calculate the hp loss. My do while loop is wrong and i need help to make it functional. I want when health goes lower than 0 to exit the loop. When it exits the loop it will print game over. I am stuck in endless loop and i have no idea how to fix this.
This is index.php:
<?php
include 'Duel.php';
$duel = new Duel();
$duel->attack();
$duel->defend();
?>
This is my class duel:
<?php
class Duel{
public $_maxHealth = 20;
public $_currentHealth;
public $_baseAttack, $_specialAttack, $_mainAttack;
public $_specialChance, $deflectChance;
public $_defense;
function __construct()
{
echo 'begining of attack <br/>';
}
function attack()
{
$_specialChance = rand(0, 20);
$_specialAttack = 0;
if ($_specialChance < 10) {
$_specialAttack = 0;
} elseif ($_specialChance < 15) {
$_specialAttack = (int) rand(0, 5);
} elseif ($_specialChance <= 20) {
$_specialAttack = (int) rand(5, 10);
}
$_baseAttack = rand(1, 6);
$_mainAttack = $_baseAttack + $_specialAttack;
echo "Base attack is $_baseAttack: and special attack is : $_specialAttack attack is : $_mainAttack<br/>";
}
function defend()
{
$_maxHealth = 20;
do{
$deflectChance = rand(1, 10);
$deflect = 0;
if ($deflectChance < 5) {
$deflect = 0;
echo 'attack cannot be deflected';
}
elseif ($deflectChance > 5) {
$deflect = (int) rand(0, 3);
echo "attack is deflected for {$deflect} damage";
}
$_currentHealth = $_maxHealth + $deflect - 5;
echo "<br/>health is {$_currentHealth} <br/>";
}while($_currentHealth > 0);
if($_currentHealth > 0) echo "Game over";
}
} //end of class
You are always calculating $_currentHealth based on $_maxHealth, not the previous $_currentHealth.
Add before the loop:
$_currentHealth = $_maxHealth;
And change $_currentHealth = $_maxHealth + $deflect - 5; to:
$_currentHealth = $_currentHealth + $deflect - 5;
You can try returning the main attack variable from the attack function and simply call it on the defend function.
you are calculation $_mainAttack but doesn't subtract it from your health, therefore your player can't die and you end within an endless loop.

How to print factorial series?

I want to know how to print factorial in php. I have try to search on Google but unable to find result with solid prove example (Php Code). I find this forloop, i'm not satisfy with this.
for($c=3; $c>=1;$c--){
for($d=$c; $d>=1; $d--){
echo $c;
}// for ends
echo "<br />";
}// for ends
Out Put 333 22 1
I want this printed as output:
5 * 4 * 3 * 2 * 1
For calculating factorial,
function factorial($in) {
return array_product(range(1, $in));
}
and use it like,
echo factorial(5);
If you want to print factorial,
function factorial_print($in) {
return implode(' * ', array_reverse(range(1, $in)));
}
and use it like,
echo factorial_print(5);
Calculating a factorial is pretty straightforward, and doesn't need recursion either:
function factorial($x) {
$r = 1;
for ($i = 2; $i <= $x; ++$i) {
$r *= $i;
}
return $r;
}
echo factorial(10), PHP_EOL;
Try this one
function factorial($number) {
if ($number < 2) {
return 1;
} else {
return ($number * factorial($number-1));
}
}
Use following function inbuilt in PHP.
$fact1 = gmp_fact(5); // 5 * 4 * 3 * 2 * 1
echo gmp_strval($fact1) . "\n";
For more refer HERE

Memoizing fibonacci function in php

I've created a memoized function of the recursive version of fibonacci.
I use this as an example for other kinds of functions that would use memoization.
My implementation is bad since if I include it in a library, that means that the global variable is still seen..
This is the original recursive fibonacci function:
function fibonacci($n) {
if($n > 1) {
return fibonacci($n-1) + fibonacci($n-2);
}
return $n;
}
and I modified it to a memoized version:
$memo = array();
function fibonacciMemo($n) {
global $memo;
if(array_key_exists($n, $memo)) {
return $memo[$n];
}
else {
if($n > 1) {
$result = fibonacciMemo($n-1) + fibonacciMemo($n-2);
$memo[$n] = $result;
return $result;
}
return $n;
}
}
I purposely didn't use the iterative method in implementing fibonacci.
Is there any better ways to memoize fibonacci function in php? Can you suggest me better improvements? I've seen func_get_args() and call_user_func_array as another way but I can't seem to know what is better?
So my main question is: How can I memoize fibonacci function in php properly? or What is the best way in memoizing fibonacci function in php?
Well, Edd Mann shows an excellent way to implement a memoize function in php in His post
Here is the example code (actually taken from Edd Mann's post):
$memoize = function($func)
{
return function() use ($func)
{
static $cache = [];
$args = func_get_args();
$key = md5(serialize($args));
if ( ! isset($cache[$key])) {
$cache[$key] = call_user_func_array($func, $args);
}
return $cache[$key];
};
};
$fibonacci = $memoize(function($n) use (&$fibonacci)
{
return ($n < 2) ? $n : $fibonacci($n - 1) + $fibonacci($n - 2);
});
Notice that the global definition it's replaced thanks to function clousure and PHP's first-class function support.
Other solution:
You can create a class containing as static members: fibonnacciMemo and $memo. Notice that you don't longer have to use $memo as a global variable, so it won't give any conflict with other namespaces.
Here is the example:
class Fib{
//$memo and fibonacciMemo are static members
static $memo = array();
static function fibonacciMemo($n) {
if(array_key_exists($n, static::$memo)) {
return static::$memo[$n];
}
else {
if($n > 1) {
$result = static::fibonacciMemo($n-1) + static::fibonacciMemo($n-2);
static::$memo[$n] = $result;
return $result;
}
return $n;
}
}
}
//Using the same method by Edd Mann to benchmark
//the results
$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000249
$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000016 (now with memoized fibonacci)
//Cleaning $memo
Fib::$memo = array();
$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000203 (after 'cleaning' $memo)
Using this, you avoid the use of global and also the problem of cleaning the cache. Althought, $memo is not thread save and the keys stored are no hashed values.
Anyways, you can use all the php memoize utilites such as memoize-php
i think... this should to to memoize a fibonacci:
function fib($n, &$computed = array(0,1)) {
if (!array_key_exists($n,$computed)) {
$computed[$n] = fib($n-1, $computed) + fib($n-2, $computed);
}
return $computed[$n];
}
some test
$arr = array(0,1);
$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000068
$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000005
//Cleaning $arr
$arr = array(0,1);
$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000039
Another solution:
function fib($n, &$memo = []) {
if (array_key_exists($n,$memo)) {
return $memo[$n];
}
if ($n <=2 ){
return 1;
}
$memo[$n] = fib($n-1, $memo) + fib($n-2, $memo);
return $memo[$n];
}
Performance:
$start = microtime(true);
fib(100);
echo sprintf("%f\n", microtime(true) - $start);
// 0.000041
This's an implementation of memoize a fibonacci:
function fib(int $n, array &$memo = [0,1,1]) : float {
return $memo[$n] ?? $memo[$n] = fib($n-1, $memo) + fib($n-2, $memo);
}
Call
echo fib(20); // 6765
function fibMemo($n)
{
static $cache = [];
//print_r($cache);
if (!empty($cache[$n])) {
return $cache[$n];
} else {
if ($n < 2) {
return $n;
} else {
$p = fibMemo($n - 1) + fibMemo($n - 2);
$cache[$n] = $p;
return $p;
}
}
}
echo fibMemo(250);

Refactoring comparision/operators blocks to DRY up and reduce C.R.A.P level

I set out to make a small project around a bounch of classes that return generators (php 5.5).
The main motivation for the small project was to expand on my TDD journey, fiddle with generators and have a package I could throw on packagist for later use.
The current state of the whole "project" can be found at Github
All tests are green, the methods does what I want. Now I want to refactor as I there is lots of dublication.
/**
* Returns a Generator with a even range.
*
* getEven(10); // 10,12,14,16,18,20,22 ...
* getEven(null, 10); // 10,8,6,4,2,0,-2,-4 ...
* getEven(10, null, 2); // 10,6,2, -2 ...
* getEven(10,20); // 10,12,14,16,18,20
* getEven(20,10); // 20,18,16,14,12,10
* getEven(10,20,2); // 10,14,18
*
* #param int|null $start
* #param int|null $end
* #param int $step
* #throws InvalidArgumentException|LogicException
* #return Generator
*/
public function getEven( $start = null, $end = null, $step = 1 )
{
// Throws LogicException
$this->throwExceptionIfAllNulls( [$start, $end] );
$this->throwExceptionIfInvalidStep($step);
// Throws InvalidArgumentException
$this->throwExceptionIfNotNullOrInt( [$start, $end] );
// infinite increase range
if(is_int($start) && is_null($end))
{
// throw LogicException
$this->throwExceptionIfOdd($start);
$Generator = function() use ($start, $step)
{
for($i = $start; true; $i += $step * 2)
{
yield $i;
}
};
}
// infinite decrease range
elseif(is_int($end) && is_null($start))
{
// throws LogicException
$this->throwExceptionIfUneven($end);
$Generator = function() use ($end, $step)
{
for($i = $end; true; $i -= $step * 2)
{
yield $i;
}
};
}
// predetermined range
else
{
// throws LogicException
$this->throwExceptionIfUneven($start);
$this->throwExceptionIfUneven($end);
// decrease
if($start >= $end)
{
$Generator = function() use ($start, $end, $step)
{
for($i = $start; $i >= $end; $i -= $step * 2)
{
yield $i;
}
};
}
// increase
else
{
$Generator = function() use ($start, $end, $step)
{
for($i = $start; $i <= $end; $i += $step * 2)
{
yield $i;
}
};
}
}
return $Generator();
}
The class also has a method named getOdd (and yes it looks alot like it ;) )
The main dublication is the closures $Generator = function() ... and the difference is mostly operators such as + - * / and arguments in the for loop. This is mainly the same in the rest of th class.
I read Dynamic Comparison Operators in PHP and come to the conclusion that there is no native method like compare(...)
Should I make a private/protected method for comparison. If so should I make a new class/function for this? I do not think it belongs in the current class.
Is it something else I am missing, I am unsure on how to DRY this up, in a proper way?
Btw. iknow a getEven, getOdd is kinda silly when i got a getRange With step function, but it is a more general refactoring / pattern question.
Update
#github the getEven and getOdd are now removed...
The code below has not been tested or verified to work, but I have faith in it and at least it shows one possible way of removing the multiple generator functions.
As you state yourself, the duplication you are trying to remove is mainly in the generator function. If you look into this you can see that every generator function you have can be written as this:
function createGenerator($index, $limit, $step) {
return function() use($index, $limit, $step) {
$incrementing = $step > 0;
for ($i = $index; true; $i += 2 * $step) {
if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
yield $i;
}else {
break;
}
}
};
}
In order to utilize this you need to do some magic with the input arguments and it helps (at least makes it pretty) to define some constants. PHP allready got a PHP_INT_MAX constant holding the greatest value possible for an integer, however it does not got a PHP_INT_MIN. So I would define that as a constant of its own.
define('PHP_INT_MIN', ~PHP_INT_MAX);
Now lets take a look at the four cases in your function.
1) Infinite increase range
Infinte is a rather bold claim here, if we change it to "greatest value possible given the constraints of an int" we get a finite range from $index to PHP_INT_MAX, hence by setting $limit = PHP_INT_MAX; the above mentioned generator function will still be the same.
//$limit = PHP_INT_MAX;
createGenerator($index, PHP_INT_MAX, $step);
2) Infinite decrease range
The same argument as above can again be used here, but with a negativ $step and swapping $index and $limit;
//$index = $limit;
//$limit = PHP_INT_MIN;
//$step *= -1;
createGenerator($limit, PHP_INT_MIN, -1 * $step);
3) Predetermined decreasing range
Swap and negate once again.
//$temp = $index;
//$index = $limit;
//$limit = $temp;
//$step *= -1;
createGenerator($limit, $index, -1 * $step);
4) Predetermined increasing range
Well this is just the default case, where all arguments are given. And nothing needs to change.
createGenerator($index, $limit, $step);
The revised code
public function getEven($index = null, $limit = null, $step = 1) {
// Throws LogicException
$this->throwExceptionIfAllNulls([$index, $limit]);
$this->throwExceptionIfInvalidStep($step);
// Throws InvalidArgumentException
$this->throwExceptionIfNotNullOrInt([$index, $limit]);
//Generator function
function createGenerator($index, $limit, $step) {
return function() use($index, $limit, $step) {
$incrementing = $step > 0;
for ($i = $index; true; $i += 2 * $step) {
if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
yield $i;
}else {
break;
}
}
};
}
// infinite increase range
if (is_int($index) && is_null($limit)) {
// throw LogicException
$this->throwExceptionIfodd($index);
return createGenerator($index, PHP_INT_MAX, $step);
}
// infinite decrease range
elseif (is_int($limit) && is_null($index)) {
// throws LogicException
$this->throwExceptionIfodd($limit);
return createGenerator($limit, PHP_INT_MIN, -1*$step);
}
// predetermined range
else {
// throws LogicException
$this->throwExceptionIfodd($index);
$this->throwExceptionIfodd($limit);
// decrease
if ($index >= $limit) {
return createGenerator($limit, $index, -1 * $step);
}
return createGenerator($index, $limit, $step);
}
}

Optimising this function

I have a script which calls this function more than 100k times, so I am looking for anyway to squeeze a bit more performance out of it.
Can you suggest optimisations or an alternate method for calculating standard deviation in PHP?
function calcStandardDev($samples){
$sample_count = count($samples);
for ($current_sample = 0; $sample_count > $current_sample; ++$current_sample) $sample_square[$current_sample] = pow($samples[$current_sample], 2);
return sqrt(array_sum($sample_square) / $sample_count - pow((array_sum($samples) / $sample_count), 2));
}
$samples[$current_sample] * $samples[$current_sample]
is going to be faster than
pow($samples[$current_sample], 2)
because it doesn't have the overhead of the function call.
Then you can also simplify
pow((array_sum($samples) / $sample_count), 2));
to prevent calling the pow() function again
To avoid array_sum($samples) being called twice as a result of that change, calculate it once and store to a var before the loop, then just reference that var in the formula.
EDIT
function calcStandardDev($samples){
$sample_count = count($samples);
$sumSamples = array_sum($samples);
for ($current_sample = 0; $sample_count > $current_sample; ++$current_sample)
$sample_square[$current_sample] = $samples[$current_sample] * $samples[$current_sample];
return sqrt(array_sum($sample_square) / $sample_count - ( ($sumSamples / $sample_count) *
($sumSamples / $sample_count)
)
);
}
foreach by referance is faster than for, an you already have a loop, you can calculate "sum" in this loop. and $x*$x is so faster then pow($x,2);
there are some functions comparations. hope to help.
Your Function microtime = ~ 0.526
Second Function = ~ 0.290
<?php
function calcStandardDev($samples)
{
$sample_count = count($samples);
for ($current_sample = 0; $sample_count > $current_sample; ++$current_sample)
$sample_square[$current_sample] = pow($samples[$current_sample], 2);
return sqrt(array_sum($sample_square) / $sample_count - pow((array_sum($samples) / $sample_count), 2));
}
function calcStandardDev2($samples)
{
$sample_count = count($samples);
$sum_sample_square = 0;
$sum_sample = 0;
foreach ($samples as &$sample)
{
$sum_sample += $sample;
$sum_sample_square += $sample * $sample;
}
return sqrt($sum_sample_square / $sample_count - pow($sum_sample / $sample_count,2));
}
function calcStandardDev3($samples)
{
$sample_count = count($samples);
$sum_sample_square = 0;
$sum_sample = 0;
foreach ($samples as &$sample)
{
$sum_sample += $sample;
$sum_sample_square += pow($sample ,2);
}
return sqrt($sum_sample_square / $sample_count - pow($sum_sample / $sample_count,2));
}
echo "<pre>";
$samples = range(2,100000);
$start = microtime(true);
echo calcStandardDev($samples)."\r\n";
$end = microtime(true);
echo $end - $start ."\r\n";
echo "-------\r\n";
$start = microtime(true);
echo calcStandardDev2($samples)."\r\n";
$end = microtime(true);
echo $end - $start."\r\n";
echo "-------\r\n";
$start = microtime(true);
echo calcStandardDev3($samples)."\r\n";
$end = microtime(true);
echo $end - $start;
echo "-------\r\n";
?>
Replace both call to array_sum by calculating the respective values yourself. That way you just walk through your array one time instead of three times.
function calcStandardDev($samples){
$sample_count = count($samples);
$sum = 0;
$sum_sqaure = 0;
for ($current_sample = 0; $sample_count > $current_sample; ++$current_sample) {
$sum_square += pow($samples[$current_sample], 2);
$sum += $samples[$current_sample];
}
return sqrt( $sum_square / $sample_count - pow( $sum / $sample_count, 2));
}

Categories