Random Float against an specific amount with 100 days php - php

I am trying to generate random values against '100' amount of value for 100 days. I am trying this way with for loop:
$total_days = 100;
$total_amount = 100;
$arr = array();
for($i = 0; $i < $total_days; ++$i)
{
$arr[] = rand(0.0, 1000.0);
}
$actual_sum = array_sum($arr);
for($i = 0; $i < $total_days; ++$i)
{
$arr[$i] *= $total_amount /$actual_sum;
//echo $arr[$i]."<BR><BR>";
$value = $arr[$i];
$genpackageroi = array(
'userid' => $userid,
'pkgid' => $pkgid,
'pkgcount' => $pkcount,
'amount' => $value,
'created_at' => $created_at,
'updated_at' => $updated_at,
);
DB::table('genpackageroi')->insert($genpackageroi);
}
The random generated value is like 0.1245444 OR 1.1245545, lots of numbers after decimal. While I just want this to be generated in float value like 1.21, 0.10, 2.25 like so. I it possible to do that?

But I want to generate the random numbers in round numbers. Because It
dose not matches the actual amount after generating all the random
numbers if we sum the generated random numbers. Like if I generates
the random numbers for amount of 100, then the sum of all generated
random numbers should 100. This is what actually I want.
What I gathered from your statement is that you want 100 floating numbers with 2 decimal places, and the sum of all these numbers should add up to 100.
This certainly is not the cleanest solution, but here goes:
$total_days = 100;
$total_amount = 100;
$arr = array();
for($i = 0; $i < $total_days; ++$i)
{
$arr[] = rand(0.0, 1000.0);
}
$actual_sum = array_sum($arr);
for($i = 0; $i < $total_days; ++$i)
{
$y = $arr[$i] * ($total_amount /$actual_sum);
$arr[$i] = round($y, 2); //round the numbers to 2 dp. Sum of all numbers could be greater than 100.
}
//hack, logic explained below
$maxElementKeys = array_keys($arr, max($arr)); //get max element's key
unset($arr[$maxElementKeys[0]]); //remove the max element from array, now the array contains 99 elements
$arr = array_values($arr); //rebase the keys
for($i = 0; $i < $total_days; ++$i)
{
if($i < ($total_days - 1) )
$value = $arr[$i];
else
$value = $total_amount - array_sum($arr); //to get the 100th number, subtract from 100 to ensure the total always adds up to 100
$genpackageroi = array(
'userid' => $userid,
'pkgid' => $pkgid,
'pkgcount' => $pkcount,
'amount' => $value,
'created_at' => $created_at,
'updated_at' => $updated_at,
);
DB::table('genpackageroi')->insert($genpackageroi);
}
The logic here is that round all numbers to 2dps. The total of which would in most cases exceed 100 by a few dps.
Next, get the max random number in the array and remove it from the array to make it an array containing 99 elements. The max number is removed to ensure that the sum of the new array stays below well clear of 100. So, to get the 100th element, get the sum of the new array and subtract that value from 100. Now, the total of all elements of the array and the 100th element that was just calculated should add up to exactly 100, with negligible chance for failure.

Related

Generate a random number from 2 ranges of numbers in php

My current code generates 116 random numbers between the range of 34 and 76 and puts them into an array called $common.
for($i = 0; $i < 116; $i++){
$common[] = mt_rand(34, 76);
}
Is it possible to generate a random number from 2 groups of numbers? For instance, I want to have it pick 116 random numbers between 1-22 and 34-76.
1. Here's one way:
for($i = 0; $i < 116; $i++){
$common[] = array(mt_rand(1, 22), mt_rand(34, 76))[mt_rand(0, 1)];
}
Create an array of two random numbers, one from each range
Randomly pick array index 0 or 1
2. Well I'm bored, here's another:
$range = array_merge(range(1, 22), range(34, 76));
for($i = 0; $i < 116; $i++){
$common[] = $range[array_rand($range)];
}
Create an array of the numbers in the two ranges
Loop and randomly select from the range
In the second example you can also use:
$common[] = $range[mt_rand(0, count($range)-1)];
3. I'm waiting for lunch so here's another, similar to the first:
for($i = 0; $i < 116; $i++){
$common[] = mt_rand(0, 1) ? mt_rand(1, 22) : mt_rand(34, 76);
}
If random falsey 0 then generate from the first range
If random truthy 1 then generate from the second range

Do an action after two rounds of a for loop

I've got a $identifier, $start_number and $end_number.
The start number is the number where the for loop should start
counting from
The end number is where the loop should stop counting
The identifier determinates how much is getting added to the start number
This for loops looks something like this:
$start_number = 102;
$end_number = 1051;
$identifier = 24;
for($i = $start_number; $i <= $end_number; $i += $identifier) {
//The first two times, add 1 to the identifier
//The second two times (we're at 4 now) add 5 to the identifier
//The third two times (were at 6 now) add 10 to the identifier
//The fourth two times (we're at 8 now) add 20 to the identifier
//etc...
}
I want it to add a dynamic number (which changes) to the $identifier each 2 times it loops, how do i do this?
Just keep track of where you are in your loop by using a counter. Then you can use the modulus operator to determine if it an even number iteration. You can add the appropriate value by using an array to store the values to add to $identifier with the count being the key to get your correct value.
$start_number = 102;
$end_number = 1051;
$identifier = 24;
$add = array(
2 => 1,
4 => 5,
6 => 10,
8 => 20
);
$count = 1;
for($i = $start_number; $i <= $end_number; $i += $identifier) {
if ($count % 2 === 0) {
$identifier += $add[$count];
}
$count++;
}
for($i = $identifier; $i <= $end_number; $i += $identifier) {
if($i%2 == 0){
//do your work
}
}

Generating random values and keeping track of their sum

I have more than 200 entries in a database table and I would like to generate a random value for each entry, but in the end, the sum of entries values must equal 100. Is it possible to do this using a for loop and rand() in PHP?
You could simply normalize a set of numbers, like:
$numbers = array();
for ($i = 0; $i < 200; $i += 1) {
$numbers[] = rand();
}
$sum = array_sum($numbers);
// divide $sum by the target sum, to have an instant result, e.g.:
// $sum = array_sum($numbers) / 100;
// $sum = array_sum($numbers) / 42;
// ...
$numbers = array_map(function ($n) use($sum) {
return $n / $sum;
}, $numbers);
print_r($numbers);
print_r(array_sum($numbers)); // ~ 1
demo: http://codepad.viper-7.com/RDOIvX
The solution for your problem is to rand number from 0 to 200 then put in array, then sum the values and divide it by 200 after that. Loop through elements and divide every element by result of previous equatation it will give you the answer
$sum = 0;
$max = 100; //max value to be sumed
$nr_of_records = 200; // number of records that should sum to $max
$arr = array();
for($i=0;$i<$nr_of_records;++$i)
{
$arr[$i] = rand(0,$max);
}
$div = array_sum($arr) / $max;
for($i=0;$i<$nr_of_records;++$i)
{
$arr[$i] /= $div;
echo $arr[$i].'<br>';
}
echo array_sum($arr);
Created living example
How exact has the 100 to be? Just curious, because all hints end at using floating point values, which tend to be inacurate.
I'd propose using fractions... lets say 10000 fractions, each count 1/100 point (10000 * 1/100 = 100 points). Distribute 10000 points to 200 elements, using integers - and be absolutely sure, that the sum of all integers divided by 10000 is 100. There is no need for floats, just think around the corner...
Do a little over/under:
$size = 200;
$sum = 100;
$places = 3;
$base = round($sum/$size, $places);
$values = array_fill(0, $size, $base);
for($i=0; $i<$size; $i+=2) {
$diff = round((rand()/getrandmax()) * $base, $places);
$values[$i] += $diff;
$values[$i+1] -= $diff;
}
//optional: array_shuffle($values);
$sum = 0;
foreach($values as $item) {
printf("%0.3f ", $item);
$sum += $item;
}
echo $sum;
Output:
0.650 0.350 0.649 0.351 0.911 0.089 0.678 0.322 0.566 0.434 0.563 0.437 0.933 0.067 0.505 0.495 0.503 0.497 0.752 0.248 0.957 0.043 0.856 0.144 0.977 0.023 0.863 0.137 0.766 0.234 0.653 0.347 0.770 0.230 0.888 0.112 0.637 0.363 0.716 0.284 0.891 0.109 0.549 0.451 0.629 0.371 0.501 0.499 0.652 0.348 0.729 0.271 0.957 0.043 0.769 0.231 0.767 0.233 0.513 0.487 0.647 0.353 0.612 0.388 0.509 0.491 0.925 0.075 0.797 0.203 0.799 0.201 0.588 0.412 0.788 0.212 0.693 0.307 0.688 0.312 0.847 0.153 0.903 0.097 0.843 0.157 0.801 0.199 0.538 0.462 0.954 0.046 0.541 0.459 0.893 0.107 0.592 0.408 0.913 0.087 0.711 0.289 0.679 0.321 0.816 0.184 0.781 0.219 0.632 0.368 0.839 0.161 0.568 0.432 0.914 0.086 0.991 0.009 0.979 0.021 0.666 0.334 0.678 0.322 0.705 0.295 0.683 0.317 0.869 0.131 0.837 0.163 0.792 0.208 0.618 0.382 0.606 0.394 0.574 0.426 0.927 0.073 0.661 0.339 0.986 0.014 0.759 0.241 0.547 0.453 0.804 0.196 0.681 0.319 0.960 0.040 0.708 0.292 0.558 0.442 0.605 0.395 0.986 0.014 0.621 0.379 0.992 0.008 0.622 0.378 0.937 0.063 0.884 0.116 0.840 0.160 0.607 0.393 0.765 0.235 0.632 0.368 0.898 0.102 0.946 0.054 0.794 0.206 0.561 0.439 0.801 0.199 0.770 0.230 0.843 0.157 0.681 0.319 0.794 0.206 100
The rounding gets a bit squiffy if you're not using nice numbers like 100 and 200, but never more than 0.1 off.
Original question yesterday had exactly 200 entries and the sum "not greater than 100".
My original answer from yesterday:
Use random numbers not greater than 0.5 to be sure.
Alternatively, depending on how "random" those numbers need to be (how
much correlation is allowed), you could keep a running total, and if
it gets disproportionately high, you can mix in a bunch of smaller
values.
Edit:
Way to go changing the question, making me look stupid and get downvoted.
To get the exact sum you have to normalize, and better use exact fractions instead of floats to avoid rounding errors.

Making number ranges adapt to the smallest and largest numbers

I have two numbers (for this example lets say 695 is the smallest and 36000 is the largest). Currently once I know the min and the max I divide the difference by ten and then cycle it through a loop to crate the ranges. It looks something like this:
$min = (int)$min-1;
$max = (int)$max;
$diff = ($max - $min) / 10;
$range = array();
for ( $i=1; $i<10; $i++){
$range[] = array(
"low"=>($i==1? $min: ($i*$diff) + $min),
"high"=>($i+1)*$diff + $min
);
}
This works great when the numbers are 695-36000. When the numbers get close together it becomes a little cumbersome to have 10 ranges. For example, the min is 34000 and the max is 36000 the ranges would be 34000-34200, 34200-34400, etc, etc.
Ideally if 695-36000 is 10 different ranges than 34000-36000 would be one range.
What would be an easy way to calculate how many ranges should show up and what those ranges should be?
Here is something to consider:
$diff = ($max - $min) / 10;
$range_max = 2000;
if($diff>$range_max) $range_size = $range_max;
else $range_size = $diff;
$creep = ($max-$min-$range_size)/9-$diff;
$range = array();
for($i=0; $i<10; $i++) {
$range[] = array (
'low' => (int)(($diff+$creep)*$i+$min),
'high' => (int)(($diff+$creep)*$i+$min+$range_size)
);
}

Smaller number generation from a large number and total of smaller number must be a large number

How can I generate fix smaller random numbers from a large number. Addition of these smaller numbers must be equal to large number. Suppose I want to generate 400 random number and addition of these smaller number = e.g. 1,000,000. every number should be unique and have any value assign to it. Like Number 1=1000 and number 2 may contain only 5. But total of all the number must be a large number. Is there any algorithm to do this kind of operation in php?
function array_generate_sum($n, $total)
{
$sum = 0;
$arr = array();
for( ; $n >= 0; $n--)
{
$current = $n == 0 ? $total - $sum : mt_rand(1, $total - $sum - $n);
$sum += $current;
$arr[] = $current;
}
return $arr;
}
// Generate an array of 5 values whose sum is 30
array_generate_sum(5, 30);

Categories