Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 12 months ago.
Improve this question
i want to generate 630 random numbers between 1 and 20. So far, so good. However, the sum of the numbers between 1 and 20 of these 630 produced should not exceed 3000. I could not write such an algorithm. Could you help?
Your problem is that 3000 / 630 = 4.76 which is far from the average number between 1-20. What we can do is constrain the random numbers' ranges to accommodate this, and get a distribution slant with an average sum approximate to the maximum sum.
Given the required maximum average of 4.76, we can for example generate 1/4 of the random numbers as between 1-20, and 3/4 as between 1-4 to slant the distribution:
function get_slanted_rands() {
$rands = [];
for($i = 1; $i <= 630; $i++) {
// For every 4th iteration, get random from the full range
$rands[] = $i % 4 === 0
? mt_rand(1,20)
: mt_rand(1,4);
}
$sum = array_sum($rands);
// If the sum is over the max, recall:
if($sum > 3000) {
return get_slanted_rands();
}
// Log the average for debugging:
$avg = round($sum / count($rands), 2);
return compact('sum', 'avg', 'rands');
}
Notice that the function checks the sum and recalls itself on the odd occasion that the sum exceeds 3000. On a typical call, the sum 3000 isn't exceeded with the above "calibration" — since we split 1:3 in favor of the lower range. But random is random, so there's no "average" guaranteed.
Let's test-drive this contraption for 100 iterations and see what we get:
$test = [];
for($t = 1; $t <= 100; $t++) {
$test[] = get_slanted_rands();
}
$sums = array_column($test, 'sum');
rsort($sums);
echo "==== SUMS ====\n" . implode(', ', $sums) . "\n\n";
$avgs = array_column($test, 'avg');
rsort($avgs);
echo "==== AVGS ====\n" . implode(', ', $avgs);
This yields for the following (reverse sorted), sums between 2600+ and 3000 and averages between 4.75 and 4.19. (On average, ~1:100 iterations exceeds 3000 and is recalled.)
==== SUMS ====
2993, 2985, 2975, 2969, 2959, 2947, 2941, 2933, 2926, 2919, 2919, 2917, 2911, 2909, 2908, 2904, 2903, 2901, 2894, 2894, 2892, 2886, 2884, 2884, 2882, 2881, 2879, 2876, 2871, 2869, 2867, 2863, 2861, 2853, 2852, 2852, 2851, 2851, 2850, 2848, 2844, 2843, 2842, 2838, 2838, 2837, 2837, 2837, 2836, 2834, 2834, 2826, 2826, 2825, 2820, 2814, 2813, 2812, 2802, 2801, 2799, 2799, 2799, 2798, 2796, 2795, 2792, 2789, 2789, 2787, 2784, 2784, 2784, 2783, 2778, 2778, 2776, 2768, 2767, 2764, 2762, 2754, 2754, 2747, 2743, 2737, 2736, 2728, 2727, 2724, 2723, 2721, 2719, 2718, 2707, 2706, 2690, 2673, 2642
==== AVGS ====
4.75, 4.74, 4.72, 4.71, 4.7, 4.68, 4.67, 4.66, 4.64, 4.63, 4.63, 4.63, 4.62, 4.62, 4.62, 4.61, 4.61, 4.6, 4.59, 4.59, 4.59, 4.58, 4.58, 4.58, 4.57, 4.57, 4.57, 4.57, 4.56, 4.55, 4.55, 4.54, 4.54, 4.53, 4.53, 4.53, 4.53, 4.53, 4.52, 4.52, 4.51, 4.51, 4.51, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.49, 4.49, 4.48, 4.48, 4.47, 4.47, 4.46, 4.45, 4.45, 4.44, 4.44, 4.44, 4.44, 4.44, 4.44, 4.43, 4.43, 4.43, 4.42, 4.42, 4.42, 4.42, 4.42, 4.41, 4.41, 4.41, 4.39, 4.39, 4.39, 4.38, 4.37, 4.37, 4.36, 4.35, 4.34, 4.34, 4.33, 4.33, 4.32, 4.32, 4.32, 4.32, 4.31, 4.3, 4.3, 4.27, 4.24, 4.19
You can always tweak the slanting condition if you feel it should on average be closer to 3000... Or simply add a recall condition with a "minimum sum" check and "brute force" it. But this suffices to give you the general idea: You need to generate random numbers of a limited range in proportion to what your "average for target sum" demands. Demo at 3v4l: https://3v4l.org/fG74X
Finally, a sample of the sort of distribution you get. Given as "number" => "instances" in a set of 630. It's heavily slanted to 1, 2, 3, 4 there — and that's how it must be to meet your specs!
$nums = get_slanted_rands();
$distribution = array_count_values($nums['rands']);
arsort($distribution);
print_r($distribution);
// yields:
[3] => 143
[4] => 125
[2] => 125
[1] => 110
[8] => 13
[9] => 12
[18] => 12
[19] => 10
[11] => 9
[5] => 8
[17] => 8
[14] => 7
[12] => 7
[10] => 7
[16] => 7
[6] => 7
[15] => 6
[7] => 6
[20] => 5
[13] => 3
$result = [];
$sumCurrent = 0;
$barrier = 3000 / 630;
for ($i = 1; $i <= 630; $i++) {
$sumBarrier = intval($i * $barrier);
while (true) {
$number = random_int(1, 20);
if ($sumCurrent + $number <= $sumBarrier) {
break;
}
}
$result[] = $number;
$sumCurrent += $number;
}
$sum = array_sum($result);
$occurrences = array_count_values($result);
uksort($occurrences, fn($key1, $key2) => $key1 <=> $key2);
print_r($result); echo "\n";
print_r($sum); echo "\n";
print_r($occurrences); echo "\n";
I'm trying to figure out how to calculate the median of an array of randomly generated numbers. I have the array all set up, but I'm having trouble putting together a function for the calcuation.
This is what I have so far:
//array
$lessFifty = array();
$moreFifty = array();
//number generation
for ($i = 0; $i<=30; $i++) {
$number = rand(0, 100);
//Sorting <50>
if ($number < 50 ) {
$lessFifty[] = $number;
} else {
$moreFifty[] = $number;
}
}
echo print_r($lessFifty);
echo "<br>" ;
echo print_r($moreFifty);
//Average
echo "<p> Average of values less than fifty: </p>";
print array_sum($lessFifty) / count($lessFifty) ;
echo "<p> Average of values greater than fifty: </p>" ;
print array_sum($moreFifty) / count($moreFifty) ;
//Median
$func = function (median ($array, $output = $median)){
if(!is_array($array)){
return FALSE;
}else{
switch($output){
rsort($array);
$middle = round(count($array) 2);
$total = $array[$middle-1];
break;
return $total;
}
}
echo $func ;
I'm pretty sure that I'm doing this median section completely wrong. I'm just learning and its proving to be a challenge.
Be careful about how you write your for() loop. If you want 30 entries, then you should not use <= or you will end up with 31 because $i starts with 0.
Build an array of the random numbers, then sort them.
Then determine if you have a central entry (odd array length) or if you need to average the middle two entries (even array length).
Here is a modern implementation of a median method posted in 2022 on CodeReview.
Code: (Demo)
$limit = 30; // how many random numbers do you want? 30 or 31?
for ($i = 0; $i < $limit; ++$i) {
$numbers[] = rand(0, 100);
}
var_export($numbers);
//echo "\n---\nAverage: " , array_sum($numbers) / $limit;
echo "\n---\n";
sort($numbers);
$count = sizeof($numbers); // cache the count
$index = floor($count/2); // cache the index
if (!$count) {
echo "no values";
} elseif ($count & 1) { // count is odd
echo $numbers[$index];
} else { // count is even
echo ($numbers[$index-1] + $numbers[$index]) / 2;
}
Possible Output:
array (
0 => 27,
1 => 24,
2 => 84,
3 => 43,
4 => 8,
5 => 51,
6 => 60,
7 => 86,
8 => 9,
9 => 48,
10 => 67,
11 => 20,
12 => 44,
13 => 85,
14 => 6,
15 => 63,
16 => 41,
17 => 32,
18 => 64,
19 => 73,
20 => 43,
21 => 24,
22 => 15,
23 => 19,
24 => 9,
25 => 93,
26 => 88,
27 => 77,
28 => 11,
29 => 54,
)
---
43.5
After sorting, elements [14] and [15] hold 43 and 44 respectively. The average of these "middle two" values is how the result is determined. (Hardcoded numbers demo)
If you want a short, inflexible, hardcoded snippet, then you can use 30 and 14 and 15 as your predetermined size and indexes.
for ($i = 0; $i < 30; ++$i) {
$numbers[] = rand(0, 100);
}
sort($numbers);
echo ($numbers[14] + $numbers[15]) / 2;
Whenever someone adds an 'item' on my website, I log the item number in a file called items_added.log. I want to make a script which shows me the 5 most commonly added items. Let's say this is my array:
1 => 100
2 => 200
3 => 300
4 => 400
5 => 500
6 => 600
7 => 700
8 => 800
9 => 900
10 => 1000
In this case, I would want to print this array;
10 => 1000
9 => 900
8 => 800
7 => 700
6 => 600
5 => 500
How can I do so? Here's my code so far:
<?php
$file = 'items_added.log';
$content = file_get_contents($file);
$arrItems = explode("\n", $content);
function array_count_values_of($value, $array) {
$counts = array_count_values($array);
return $counts[$value];
}
$itemCounts = array();
foreach($arrItems as $item) {
$itemCounts[$item] = array_count_values_of($item, $arrItems);
}
// Somehow print the 5 largest values (the 5 most commonly added items)
// $itemCounts is an array which contains all items ever added & how many times they have been added
// The structure is ItemNumber => Frequency
?>
You should use arsort, this sorts the array on the value (but in reverse, so from highest to lowest).
arsort($itemCounts);
$top5 = array_slice($itemCounts, 0, 5);
Then you have the $top5 of your array in the variable $top5.
Try arsort and array_splice.
No need to use any custom code.
<?php
$arr = [
1 => 100,
2 => 200,
3 => 300,
4 => 400,
5 => 500,
6 => 600,
7 => 700,
8 => 800,
9 => 900,
10 => 1000
];
arsort($arr);
$arr = array_slice($arr, 0, 5, 1);
print_r($arr);
?>
Hope that helps. Please let me know in case of further issues.
<?php
$arr = [
1 => 100,
2 => 200,
3 => 300,
4 => 400,
5 => 500,
6 => 600,
7 => 700,
8 => 800,
9 => 900,
10 => 1000
];
rsort($arr);
print_r($arr);
?>
I have an array with integers of values from 0 to 100. I wish to remove integers that are less than number X and keep the ones that are equal or greater than number X.
A little ugly using the clunky create_function, but straight forward:
$filtered = array_filter($array, create_function('$x', 'return $x >= $y;'));
For PHP >= 5.3:
$filtered = array_filter($array, function ($x) { return $x >= $y; });
Set $y to whatever you want.
Smarter than generating an array that is too big then cutting it down to size, I recommend only generating exactly what you want from the very start.
range() will do this job for you without the bother of an anonymous function call iterating a condition.
Code: (Demo)
$rand=rand(0,100); // This is your X randomly generated
echo $rand,"\n";
$array=range($rand,100); // generate an array with elements from X to 100 (inclusive)
var_export($array);
Potential Output:
98
array (
0 => 98,
1 => 99,
2 => 100,
)
Alternatively, if you truly, truly want to modify the input array that you have already generated, then assuming you have an indexed array you can use array_slice() to remove elements using X to target the starting offset and optionally preserve the indexes/keys.
Code: (Demo)
$array=range(0,100);
$rand=rand(0,100); // This is your X randomly generated
echo $rand,"\n";
var_export(array_slice($array,$rand)); // reindex the output array
echo "\n";
var_export(array_slice($array,$rand,NULL,true)); // preserve original indexes
Potential Output:
95
array (
0 => 95,
1 => 96,
2 => 97,
3 => 98,
4 => 99,
5 => 100,
)
array (
95 => 95,
96 => 96,
97 => 97,
98 => 98,
99 => 99,
100 => 100,
)