out of memory issue with PHP script - php

My PHP script is running out of memory. I have put memory_get_usage( ) inside the loops and found that it is running out of memory after 6MB.
I would like to clearly explain my script and like to get suggestions from you.
I am not sure, If my script is running out of memory because of problems with code .
So, here is the algorithm for my script:
Read the input json data into an array ( approximatley 200 lines of data)
calculate the sum of the array ($sum)
3 variables $starting, $ending and $compare.( Intialize $compare = $starting)
start with $starting=$vmin , calculate scaling factor, scale the original array and calculate sum of it .That new sum is $ending.
Add 10 to $starting until the diff between $ending and $compare is 50.
repeat all the steps until $vmin reaches $vmax with an increment of $vinc
The above algorithm might look insane, But it finds an optimal solution for supply chain related optimization problem.
I would like you to see if my weak coding capabilities are the reason for the memory outage .If so, Please suggest the changes to my script.
Here is my code
$vmax = 10000;
$vmin = 1000;
$vinc = 2000;
//decode the json data
$fp = fopen("data_save3.json", "r");
$inp=file_get_contents("data_save3.json");
fclose($fp);
$inp=json_decode($inp);
//calculate the array sum
foreach($inp as $i=>$element) {
foreach($element as $j=>$sub_element) {
$sum+= $inp[$i][$j];
}
}
//start at vmin and increment it until vmax
for(;$vmin <=$vmax;) {
$starting=$vmin;
$compare = $starting;
//calculate scaling factor
$scale = $starting/$sum;
//calculate the scaled array
$sum2 = 0;
$inp_info2 = $inp;
$ending = newscale($inp_info2,$scale,$sum2);
$optimal = getClosest($starting,$ending,$compare,$sum);
echo $optimal.PHP_EOL;
$vmin = $vmin+$vinc ;
}
// function to find the closest value
$inp_info1=$inp;
function getClosest($starting,$compare,$ending,$sum,$inp) {
global $sum2;
global $compare;
global $inp_info1;
global $starting , $ending, $scale1, $sum, $inp, $inp_info2, $inp_info3,$rowsum2, $rowsum3, $rowsum4, $rowsum5;
if (abs($ending - $compare) < 50){
return $starting;
} else{
$starting = $starting +10;
$scale1 = $starting/$sum;
$inp_info1 = $inp;
$sum2 = 0;
$ending = newscale($inp_info1,$scale1,$sum2);
return getClosest($starting);
}
}
//array scaling function
function newscale($array,$scale,$sum2){
global $sum2,$scale;
foreach($array as $i=>$element) {
foreach($element as $j=>$sub_element) {
$array[$i][$j]*= $scale;
$array[$i][$j] = truncate($array[$i][$j]);
$sum2+=$array[$i][$j];
}
}
return $sum2;
}
//truncate function
function truncate($num, $digits = 0) {
$shift = pow(10, $digits);
return ((floor($num * $shift)) / $shift);
}

Related

Get lowest price on sum of combinations in given array

This code is working fine when the array length is 8 or 10 only. When we are checking this same code for more than 10 array length.it get loading not showing the results.
How do reduce my code. If you have algorithm please share. Please help me.
This program working flow:
$allowed_per_room_accommodation =[2,3,6,5,3,5,2,5,4];
$allowed_per_room_price =[10,30,60,40,30,50,20,60,80];
$search_accommodation = 10;
i am get subsets = [5,5],[5,3,2],[6,4],[6,2,2],[5,2,3],[3,2,5]
Show lowest price room and then equal of 10 accommodation; output like as [5,3,2];
<?php
$dp=array(array());
$GLOBALS['final']=[];
$GLOBALS['room_key']=[];
function display($v,$room_key)
{
$GLOBALS['final'][] = $v;
$GLOBALS['room_key'][] = $room_key;
}
function printSubsetsRec($arr, $i, $sum, $p,$dp,$room_key='')
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if ($i == 0 && $sum != 0 && $dp[0][$sum]) {
array_push($p,$arr[$i]);
array_push($room_key,$i);
display($p,$room_key);
return $p;
}
// If $sum becomes 0
if ($i == 0 && $sum == 0) {
display($p,$room_key);
return $p;
}
// If given sum can be achieved after ignoring
// current element.
if (isset($dp[$i-1][$sum])) {
// Create a new vector to store path
// if(!is_array(#$b))
// $b = array();
$b = $p;
printSubsetsRec($arr, $i-1, $sum, $b,$dp,$room_key);
}
// If given $sum can be achieved after considering
// current element.
if ($sum >= $arr[$i] && isset($dp[$i-1][$sum-$arr[$i]]))
{
if(!is_array($p))
$p = array();
if(!is_array($room_key))
$room_key = array();
array_push($p,$arr[$i]);
array_push($room_key,$i);
printSubsetsRec($arr, $i-1, $sum-$arr[$i], $p,$dp,$room_key);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
function printAllSubsets($arr, $n, $sum,$get=[])
{
if ($n == 0 || $sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
// $dp = new bool*[$n];
$dp = array();
for ($i=0; $i<$n; ++$i)
{
// $dp[$i][$sum + 1]=true;
$dp[$i][0] = true;
}
// Sum arr[0] can be achieved with single element
if ($arr[0] <= $sum)
$dp[0][$arr[0]] = true;
// Fill rest of the entries in dp[][]
for ($i = 1; $i < $n; ++$i) {
for ($j = 0; $j < $sum + 1; ++$j) {
// echo $i.'d'.$j.'.ds';
$dp[$i][$j] = ($arr[$i] <= $j) ? (isset($dp[$i-1][$j])?$dp[$i-1][$j]:false) | (isset($dp[$i-1][$j-$arr[$i]])?($dp[$i-1][$j-$arr[$i]]):false) : (isset($dp[$i - 1][$j])?($dp[$i - 1][$j]):false);
}
}
if (isset($dp[$n-1][$sum]) == false) {
return "There are no subsets with";
}
$p;
printSubsetsRec($arr, $n-1, $sum, $p='',$dp);
}
$blockSize = array('2','3','6','5','3','5','2','5','4');
$blockvalue = array('10','30','60','40','30','50','20','60','80');
$blockname = array("map","compass","water","sandwich","glucose","tin","banana","apple","cheese");
$processSize = 10;
$m = count($blockSize);
$n = count($processSize);
// sum of sets in array
printAllSubsets($blockSize, $m, $processSize);
$final_subset_room = '';
$final_set_room_keys = '';
$final_set_room =[];
if($GLOBALS['room_key']){
foreach ($GLOBALS['room_key'] as $set_rooms_key => $set_rooms) {
$tot = 0;
foreach ($set_rooms as $set_rooms) {
$tot += $blockvalue[$set_rooms];
}
$final_set_room[$set_rooms_key] = $tot;
}
asort($final_set_room);
$final_set_room_first_key = key($final_set_room);
$final_all_room['set_room_keys'] = $GLOBALS['room_key'][$final_set_room_first_key];
$final_all_room_price['set_room_price'] = $final_set_room[$final_set_room_first_key];
}
if(isset($final_all_room_price)){
asort($final_all_room_price);
$final_all_room_first_key = key($final_all_room_price);
foreach ($final_all_room['set_room_keys'] as $key_room) {
echo $blockname[$key_room].'---'. $blockvalue[$key_room];
echo '<br>';
}
}
else
echo 'No Results';
?>
I'm assuming your task is, given a list rooms, each with the amount of people it can accommodate and the price, to accommodate 10 people (or any other quantity).
This problem is similar to 0-1 knapsack problem which is solvable in polynomial time. In knapsack problem one aims to maximize the price, here we aim to minimize it. Another thing that is different from classic knapsack problem is that full room cost is charged even if the room is not completely occupied. It may reduce the effectiveness of the algorithm proposed at Wikipedia. Anyway, the implementation isn't going to be straightforward if you have never worked with dynamic programming before.
If you want to know more, CLRS book on algorithms discusses dynamic programming in Chapter 15, and knapsack problem in Chapter 16. In the latter chapter they also prove that 0-1 knapsack problem doesn't have trivial greedy solution.

Working on a probability & provably fair chest opening function, am I thinking right?

So I'm working on a "chest opening simulator" with a client of mine and I have completed the whole system except for the actual probability part.
He sent me this calculation and algorithm for how the different rarities and items should function.
Now I got completely stuck here, but from looking at this logic, if I roll a dice that is 1-100, and it lands on 2, I hit the "Super Rare" tier correct?
I made a function for rolling a dice that is between 0.00-100.00 and which is provably fair (Using a users Client seed and a random server seed + nonce for each roll) which you can find below, how would I utilize this function to determine which of the tiers I hit and what item within the tier?
public function roll()
{
$client_seed = "client_seed";
$server_seed = "server_seed";
$nonce = 0;
$secret = $client_seed."-".$nonce;
$hash = hash_hmac('sha512', $secret, $server_seed); // Hash server_seed and secret
for($i = 0; $i < strlen($hash); $i += 5)
{
$sub = substr($hash, $i, 5); //Split it
if(strlen($sub) == 5)
{
$decimal_number = hexdec($sub); // Hex to decimal. At this point we have a random number
if($decimal_number < 1000000)
{
$decimal_fourc = bcmod($decimal_number, 10000); //Get the modulus
$final_decimal = bcdiv($decimal_fourc, 100, 2); //Divide the result by 100
$obj = new \stdClass();
$obj->seeds = new \stdClass();
$obj->seeds->server = $server_seed;
$obj->seeds->client = $client_seed;
$obj->result = new \stdClass();
$obj->result->nonce = $nonce;
$obj->result->lucky_number = number_format($final_decimal, 2);
}
} else {
break;
}
}
echo "<pre>";
print_r($obj);
echo "</pre>";
}

Generate random unique code - php

How do I generate a random code/PIN. I read PHP's rand() and mt_rand() functions and it seems to do good but I found there are duplicates.
Can someone suggest or demonstrate a better way of achieving this in PHP?
my code:
$size = 1000; //handcoded pin-size
function generatePIN($size =500){
$pins = array(); $i = 0; //ini counter
//my basic algorithm for generating unique pin codes
while ($i < $size){
$pin = (mt_rand(100000,999999));
$pins[] = $pin;
++$i;
}
sort($pins);//sort in lowest to highest
return array_unique($pins);//remove duplicates
}
//call it and print or store in database
$pins = generatePIN($size);
foreach ($pins as $pin) {
echo $pin.'<br>';
}
Thank you.
A true random always has the chance to create a duplicate, because it is unconditional to the previous given randoms.
To make a duplicate less likely you can increase the random interval with mt_rand(10000000,99999999).
Or you can check if your number was a duplicate:
while ($i < $size){
$pin = (mt_rand(100000,999999));
if (array_search($pin, $pins) === FALSE) {
$pins[] = $pin;
++$i;
}
}
This will perform more poorly the bigger $size is compared to the random interval and create an endless loop if $size is bigger than it.
adding to the generated value in the loop solved the problem is its very efficient calling it each day. No matter how long the size is.
$size = 1000; //handcoded value
function generatePIN($size =500){
$pins = array(); $i = 0;
//my basic algorithm for generating unique pin codes
while ($i < $size){
$pin = (mt_rand(100000,999999)) + (mt_rand(1000,9999)) + strtotime(date('Y-m-d', strtotime('+'.$i.' week')));
$pins[] = $pin;
++$i;
}
sort($pins);//sort in lowest to highest
return array_unique($pins);//remove duplicates
}
//store in database or print it
$pins = generatePIN($size);
foreach ($pins as $pin) {
echo $pin.'<br>';
}
echo "<hr />";
echo count($pins);

PHP "Maximum execution time"

I'm trying to program my own Sine function implementation for fun but I keep getting :
Fatal error: Maximum execution time of 30 seconds exceeded
I have a small HTML form where you can enter the "x" value of Sin(x) your looking for and the number of "iterations" you want to calculate (precision of your value), the rest is PhP.
The maths are based of the "Series definition" of Sine on Wikipedia :
--> http://en.wikipedia.org/wiki/Sine#Series_definition
Here's my code :
<?php
function factorial($int) {
if($int<2)return 1;
for($f=2;$int-1>1;$f*=$int--);
return $f;
};
if(isset($_POST["x"]) && isset($_POST["iterations"])) {
$x = $_POST["x"];
$iterations = $_POST["iterations"];
}
else {
$error = "You forgot to enter the 'x' or the number of iterations you want.";
global $error;
}
if(isset($x) && is_numeric($x) && isset($iterations) && is_numeric($iterations)) {
$x = floatval($x);
$iterations = floatval($iterations);
for($i = 0; $i <= ($iterations-1); $i++) {
if($i%2 == 0) {
$operator = 1;
global $operator;
}
else {
$operator = -1;
global $operator;
}
}
for($k = 1; $k <= (($iterations-(1/2))*2); $k+2) {
$k = $k;
global $k;
}
function sinus($x, $iterations) {
if($x == 0 OR ($x%180) == 0) {
return 0;
}
else {
while($iterations != 0) {
$result = $result+(((pow($x, $k))/(factorial($k)))*$operator);
$iterations = $iterations-1;
return $result;
}
}
}
$result = sinus($x, $iterations);
global $result;
}
else if(!isset($x) OR !isset($iterations)) {
$error = "You forgot to enter the 'x' or the number of iterations you want.";
global $error;
}
else if(isset($x) && !is_numeric($x)&& isset($iterations) && is_numeric($iterations)) {
$error = "Not a valid number.";
global $error;
}
?>
My mistake probably comes from an infinite loop at this line :
$result = $result+(((pow($x, $k))/(factorial($k)))*$operator);
but I don't know how to solve the problem.
What I'm tring to do at this line is to calculate :
((pow($x, $k)) / (factorial($k)) + (((pow($x, $k))/(factorial($k)) * ($operator)
iterating :
+ (((pow($x, $k))/(factorial($k)) * $operator)
an "$iterations" amount of times with "$i"'s and "$k"'s values changing accordingly.
I'm really stuck here ! A bit of help would be needed. Thank you in advance !
Btw : The factorial function is not mine. I found it in a PhP.net comment and apparently it's the optimal factorial function.
Why are you computing the 'operator' and power 'k' out side the sinus function.
sin expansion looks like = x - x^2/2! + x^3/3! ....
something like this.
Also remember iteration is integer so apply intval on it and not floatval.
Also study in net how to use global. Anyway you do not need global because your 'operator' and power 'k' computation will be within sinus function.
Best of luck.
That factorial function is hardly optimal—for speed, though it is not bad. At least it does not recurse. It is simple and correct though. The major aspect of the timeout is that you are calling it a lot. One technique for improving its performance is to remember, in a local array, the values for factorial previously computed. Or just compute them all once.
There are many bits of your code which could endure improvement:
This statement:
while($iterations != 0)
What if $iterations is entered as 0.1? Or negative. That would cause an infinite loop. You can make the program more resistant to bad input with
while ($iterations > 0)
The formula for computing a sine uses the odd numbers: 1, 3, 5, 7; not every integer
There are easier ways to compute the alternating sign.
Excess complication of arithmetic expressions.
return $result is within the loop, terminating it early.
Here is a tested, working program which has adjustments for all these issues:
<?php
// precompute the factorial values
global $factorials;
$factorials = array();
foreach (range (0, 170) as $j)
if ($j < 2)
$factorials [$j] = 1;
else $factorials [$j] = $factorials [$j-1] * $j;
function sinus($x, $iterations)
{
global $factorials;
$sign = 1;
for ($j = 1, $result = 0; $j < $iterations * 2; $j += 2)
{
$result += pow($x, $j) / $factorials[$j] * $sign;
$sign = - $sign;
}
return $result;
}
// test program to prove functionality
$pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620;
$x_vals = array (0, $pi/4, $pi/2, $pi, $pi * 3/2, 2 * $pi);
foreach ($x_vals as $x)
{
$y = sinus ($x, 20);
echo "sinus($x) = $y\n";
}
?>
Output:
sinus(0) = 0
sinus(0.78539816339745) = 0.70710678118655
sinus(1.5707963267949) = 1
sinus(3.1415926535898) = 3.4586691443274E-16
sinus(4.7123889803847) = -1
sinus(6.2831853071796) = 8.9457384260403E-15
By the way, this executes very quickly: 32 milliseconds for this output.

PHP: Caching ordered integer partition algorithm

First: The problem's name in Wikipedia is "ordered partition of a set".
I have an algorithm which counts possible partitions. To speed it up, I use a cache:
function partition($intervalSize, $pieces) {
// special case of integer partitions: ordered integer partitions
// in Wikipedia it is: ordered partition of a set
global $partition_cache;
// CACHE START
$cacheId = $intervalSize.'-'.$pieces;
if (isset($partition_cache[$cacheId])) { return $partition_cache[$cacheId]; }
// CACHE END
if ($pieces == 1) { return 1; }
else {
$sum = 0;
for ($i = 1; $i < $intervalSize; $i++) {
$sum += partition(($intervalSize-$i), ($pieces-1));
}
$partition_cache[$cacheId] = $sum; // insert into cache
return $sum;
}
}
$result = partition(8, 4);
Furthermore, I have another algorithm which shows a list of these possible partitions. But it doesn't use a cache yet and so it's quite slow:
function showPartitions($prefix, $start, $finish, $numLeft) {
global $partitions;
if ($numLeft == 0 && $start == $finish) { // wenn eine Partition fertig ist dann in Array schreiben
$gruppen = split('\|', $prefix);
$partitions[] = $gruppen;
}
else {
if (strlen($prefix) > 0) { // nicht | an Anfang setzen sondern nur zwischen Gruppen
$prefix .= '|';
}
for ($i = $start + 1; $i <= $finish; $i++) {
$prefix .= chr($i+64);
showPartitions($prefix, $i, $finish, $numLeft - 1);
}
}
}
$result = showPartitions('', 0, 8, 4);
So I have two questions:
1) Is it possible to implement a cache in the second algorithm, too? If yes, could you please help me to do this?
2) Is it possible to write the results of the second algorithm into an structured array instead of a string?
I hope you can help me. Thank you very much in advance!
PS: Thanks for the two functions, simonn and Dan Dyer!
No, I don't think a cache will help you here because you're never actually performing the same calculation twice. Each call to showPartitions() has different parameters and generates a different result.
Yes, of course. You're basically using another level of nested arrays pointing to integers to replace a string of characters separated by pipe characters. (Instead of "A|B|C" you'll have array(array(1), array(2), array(3)).)
Try changing showPartitions() as such:
if ($numLeft == 0 && $start == $finish) { // wenn eine Partition fertig ist dann in Array schreiben
$partitions[] = $prefix;
}
else {
$prefix[] = array();
for ($i = $start + 1; $i <= $finish; $i++) {
$prefix[count($prefix) - 1][] = $i;
showPartitions($prefix, $i, $finish, $numLeft - 1);
}
}
and instead of calling it with an empty string for $prefix, call it with an empty array:
showPartitions(array(), 0, 8, 4);
Off topic: I rewrote the first function to be a little bit faster.
function partition($intervalSize, $pieces) {
// special case of integer partitions: ordered integer partitions
// in Wikipedia it is: ordered partition of a set
// CACHE START
static $partition_cache = array();
if (isset($partition_cache[$intervalSize][$pieces])) {
return $partition_cache[$intervalSize][$pieces];
}
// CACHE END
if ($pieces === 1) {
return 1;
}
if ($intervalSize === 1) {
return 0;
}
$sum = 0;
$subPieces = $pieces - 1;
$i = $intervalSize;
while (--$i) {
$sum += partition($i, $subPieces);
}
$partition_cache[$intervalSize][$pieces] = $sum; // insert into cache
return $sum;
}
Although this is a bit old, nevertheless,
a PHP Class which implements various combinatorics/simulation methods including partitions/permutations/combinations etc.. in an efficient way
https://github.com/foo123/Simulacra/blob/master/Simulacra.php
PS: i am the author

Categories