I'm making a script that handles the percentage chances of winning when the user clicks a button.
With the help of this topic php - Chance of winning. I used the following code:
function winningChance($percentage) {
if($percentage < 1 || $percentage > 100) throw new Exception('Invalid percentage');
global $result;
if(rand(1, 100) <= $percentage) {
$result = 'won';
} else {
$result = 'lost';
}
return $result;
}
echo "you have ".winningChance(50).'!';
Once this script has run, it registers the user name / last name / email and a field called winner with the $result in a SQL database
This works great, however I would like to handle multiple prizes with different percentages of winning chances.
Lets say prize1 have 20% chances of being won,
prize2 30% chances
and prize3 50% chances.
If I use winningChance(20), winningChance(30), winningChance(50) the user will have more chances of winning. How can I handle it so, the win/lost process happens in the same function for multiple prizes?
If I understand you correctly, the chance of winning depends on the price.
function getChanceOfWinning($price)
{
$chances = array(
10 => 20,
20 => 30,
30 => 50
);
if (isset($chances[$price])) {
return $chances[$price];
}
return 0; // <-- default chance
}
function calculateWinningChance($price)
{
$chance = getChanceOfWinning($price);
$calc = rand(1, 100);
if ($calc <= $chance) {
return true;
}
return false;
}
function calculateWinningChances(array $prices)
{
$results = array();
foreach($prices as $price) {
$results[$price] = calculateWinningChance($price);
}
return $results;
}
var_dump(calculateWinningChances(array(10,20, 30,40,700)));
How about this solution ?
function roll( $iChance ) {
$iCursor = rand( 0,99 );
$aModel = array();
while ( count( $aModel ) != $iChance ) {
$iRandValue = rand( 0,99 );
if ( !in_array( $iRandValue, $aModel ) ) {
$aModel[] = $iRandValue;
}
}
return in_array( $iCursor, $aModel );
}
Edit: More perfomance:
function roll( $iChance ) {
$iChance = ceil( ( $iChance > 100 ) ? 100 : (int)$iChance);
$iCursor = rand( 0, 99 );
$aModel = range( 0, 99 );
shuffle( $aModel );
return in_array( $iCursor, array_slice( $aModel, 0, $iChance ) );
}
If I understood correctly, you want to have multiple winning calculations for each user at the same time, independent of one another. There are numerous ways to do this. Modify your function so that you can pass an associative array as an argument, for example.
The array would be a map of price=>percentage values and then you do the calculations for each pair.
You'd also need to modify your result variable in an array, and on each pass just push the result of the calculation into it. You can also use an associative array here to show price=>won/lost. After you looped through all the pairs and filled up the result variable with the results, just return the variable.
Based on your last comment, this is what you need:
function winningChance($percentage) {
foreach($percentage as $p) {
if($p < 1 || $p > 100)
throw new Exception('Invalid percentage');
}
if (count($percentage) != 3)
throw new Exception('Three prizes need to be defined');
$rand = rand(1, 100); // generate the random chance only once!
if ($rand <= $percentage[0])
$result = 'won first prize';
elseif ($rand <= $percentage[1])
$result = 'won second prize';
elseif ($rand <= $percentage[2])
$result = 'won third prize';
else
$result = 'lost';
return $result;
}
And call the function like this:
//the array contains probability percentages for the first, second and third place respectively
$res = winningChance( array(20, 30, 50) );
echo "You have $res!";
// write $res to the db here
tweak your function code like,
instead of,
if(rand(1, 100) <= $percentage) {
$result = 'won';
} else {
$result = 'lost';
}
to,
if(rand(1, 100) >= 50) {
$result = 'won third price';
} else if(rand(1, 100) >= 30) {
$result = 'won second price';
}
else if(rand(1, 100) >= 20) {
$result = 'won first price';
}
else
{
$result='lost';
}
Related
I need to generate random IDs that validate against the criteria for Saudi IDs shown in this question:
Saudi Iqama/National Identity number field validation
I've tried the following code:
$random_numbers = [];
while(count($random_numbers) < 1000000000){
do {
$random_number = mt_rand(1000000000,9000000000);
}
while (in_array($random_number, $random_numbers));{
$type = substr ( $random_number, 0, 1 );
if($type != 2 && $type != 1 ) break;
$sum = 0;
for( $i = 0 ; $i<10 ; $i++ ) {
if ( $i % 2 == 0){
$ZFOdd = str_pad ( ( substr($random_number, $i, 1) * 2 ), 2, "0", STR_PAD_LEFT );
$sum += substr ( $ZFOdd, 0, 1 ) + substr ( $ZFOdd, 1, 1 );
}else{
$sum += substr ( $random_number, $i, 1 );
}
}
return $sum%10 ? break : echo $random_number;
----------
echo "<br>";
$random_numbers[] = $random_number;}
}
Disclaimer: I'm not 100% sure on the validation required etc. for Saudi ID numbers and have only briefly looked at the answers supplied in the linked question
Okay, so, my understanding is that you need to generate a random id that:
Matches the pattern/format:
[12]\d{9}
Validates against the criteria show in the linked question:
Saudi Iqama/National Identity number field validation
To do this we need to create a couple of functions; one to generate IDs and one to validate the IDs against the given criteria.
Generate the ID
Simply generating an ID is simple enough. We can use the random_int function in PHP with a loop. If we enclose the code to generate the ID inside of a do...while... loop then we can execute the code and validate the ID repeatedly until we get a valid one.
function getRandomSaudiId() : int
{
do {
$saudiId = (string) random_int(1,2);
for($i = 0; $i < 9; $i++){
$saudiId .= random_int(0,9);
}
} while(validateSaudiId($saudiId) === false);
return (int) $saudiId;
}
Validate the ID
Note: we convert to string so that we can access the numbers based on their index.
function validateSaudiId(string $id) : bool
{
$sum = 0;
for($i = 0; $i < 9; $i++){
if( $i % 2 ){
// Even number
$sum += $id[$i];
}
else{
//Odd number
$increment = $id[$i] * 2;
while($increment > 9){
$increment = (string) $increment;
$increment = $increment[0] + $increment[1];
}
$sum += $increment;
}
}
$sum = (string) $sum;
return ($sum[1] == $id[9] || $id[9] == (10 - $sum[1])) ? true : false;
}
Example use
for($i = 0; $i < 10; $i++) var_dump(getRandomSaudiId());
/*
Output:
int(2933617506)
int(2409806096)
int(1072585118)
int(2891306413)
int(1810304558)
int(2591965856)
int(1363032527)
int(1031823269)
int(1265954048)
int(2498099472)
int(1134172537)
*/
Need to make custom function to check amount with available denominations.
code i make :
$amount = 100;
$notes_aval = array(20,50,100,500,2000);//available currency notes
$is_allowed = 0; //not allowed
foreach($notes_aval as $note){
if (fmod($amount,$note) == 0) {
$is_allowed = 1;//allowed
}
}
echo $is_allowed;
But this is not working out for all cases.
For exam : i have denominations = array (20,50);
with amount 90 is not allowed, but it should be allowed by 20*2 + 50*1 = 90
in the example of denominations = array (20,50) ,if amount 1110 should be acceptable with 1110 = 20*53 + 50*1
Try both modular divisions
function validateCurrency($amount)
{
$requestdAmount = $amount;
$valueUnder = 0;
$notes = array(20, 50,100,500,2000);
$is_allowed = 0;
if(in_array($amount, $notes)){
return $is_allowed = 1;
}
$numOccurance = ceil($amount/$notes[0]);
$arraySums = [];
foreach ($notes as $key => $value) {
for ($i=1; $i <= $numOccurance; $i++) {
if($value * $i == $amount) {
return $is_allowed = 1;
}
$arraySums[$key][] = $value * $i;
}
}
for ($i=0; $i < count($arraySums); $i++) {
for ($j=$i+1; $j < count($arraySums); $j++) {
foreach ($arraySums[$i] as $key => $value) {
foreach ($arraySums[$j] as $key2 => $toBeMul) {
if($value+$toBeMul == $amount) {
return $is_allowed = 1;
}
}
}
}
}
return $is_allowed;
}
// Driver Code
$amount = 40;
$is_allowed = validateCurrency($amount);
echo $is_allowed;
die();
It will work
You need to start exchange from largest value until your amount is smaller than largest note (eg 2000). Then you go this same with lower note (eg 500), and again with lower. When amount is smaller than lowest value (eg. 20) then you cannot exchange this amount.
So:
We start with 2270
We check for largest note - it's 2000.
Now we know we have 2000 and 270 (2270 - 2000) rest
Now we check again for largest value - it's 200
So we have 2000, 200 and 70 (270 - 200) rest
Now largest not possible is 50
So we have 2000, 200, 50 and 20 (70 - 50) rest
Now largest is 20 and we have 2000, 200, 50, 20 and rest is 0
As rest is smaller than lowest note then we can stop checking.
If rest is 0 we know we can exchange, if rest is larger than 0 then we cannot. Additionally we also have list of notes we can use for exchange (2000, 200, 50, 20).
function checkDenomination($amount){
$notes = array(2000,500,100,50,20); //it's easier if they are reversed
$smallestNote = 20;
$result = [];
while($amount >= $smallestNote) { //we will repeat until we can exchange
foreach($notes as $note) {
if ($amount >= $note) { //we check for largest value we can exchange
$result[] = $note;
$amount -= $note; //as we have hit, we can deduct it from amount;
break;
}
}
}
return ($amount > 0) ? false : $result; //return false if we cannot exchange this amount or array with notes we can exchange for full amount
}
var_dump(checkDenomination(100));
var_dump(checkDenomination(23424));
var_dump(checkDenomination(25000));
var_dump(checkDenomination(222));
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.
I have a user points system which gives users points depending on some actions like selling a product or add new post etc...
I want to make a smarter PHP function to set a level for the user depending on his/her points.
Here's how I make this:
function get_user_level( $user_id ) {
$user_points = 3515 // Here I get the number of points that user have from the database
if ( $user_point >= 3000 ) {
$level = '5';
} elseif ( $user_point >= 2000 ) {
$level = '4';
} elseif ( $user_point >= 1500 ) {
$level = '3';
} elseif ( $user_point >= 1000 ) {
$level = '2';
} elseif ( $user_point >= 500 ) {
$level = '1';
} else {
$level = '0';
}
echo 'Level:' . $level;
}
The problem that my function seems very bad and not smarter I want to develop my function to upgrade user level for each 1000 point the user has (Making unlimited levels automatically).
You mean something like:
if ($user_points < 2000)
{
$level = floor($user_points / 500);
}
else
{
$level = 4 + floor(($user_points-2000)/1000);
}
Which yields level 0-4 for 0-2000 points and then one additional level every 1000 points.
function get_user_level( $user_id ) {
$user_points = 3515; // Here I get the number of points that user have from the database
$level = intval($user_points/1000);
echo $level;
}
You could use a switch statement:
<?php
function get_user_level( $user_point )
{
switch (true) {
case $user_point >= 3000:
return 5;
case $user_point >= 2000:
return 4;
case $user_point >= 1500:
return 3;
case $user_point >= 1000:
return 2;
case $user_point >= 500:
return 1;
default:
return 0;
}
}
echo get_user_level(3515); // outputs 5
See it here: https://3v4l.org/TmiAH
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.