I need a PHP function that randomly distributes an arbitrary number of darts over an arbitrary number of dartboards. It has to return the number of dartboards that have 0 darts in them, the amount of boards with 1 dart, 2 darts etc.
I have to be able to run this within a few milliseconds with millions of darts and dartboards, but an approximation is good enough. I already have a function that can give me normally distributed random values so a solution that gives the average result can be worked with.
//Example result with 1000 darts and 500 dartboards
Array
(
[0] => 62
[1] => 128
[2] => 152
[3] => 96
[4] => 40
[5] => 14
[6] => 6
[7] => 2
)
I've made a function that generates example values by procedurally distributing each dart randomly. This is for example purposes only. The production version of the function will have to use normal distribution to create results in a scalable way.
Do not tell me how to optimize this example, its for demonstration purposes only:
<?php
function distribute($items, $targets) {
$result = array();
$values = array();
for($i=0; $i<$items; $i++) {
#$values[ mt_rand(0,$targets) ] ++;
}
for($i=0;$i<$targets;$i++) {
#$result[ (int) $values[$i] ] ++;
}
ksort($result);
return $result;
}
$microtime = microtime(true);
echo '<pre>';
print_r( distribute(100000, 50000) );
print_r(microtime(true) - $microtime);
Related
Let's explain my problem!
we are coding a website for player of some games we know that the minimum of player is 2 and don't know the maximum of player.
in these games we have a leaderboard (1st,second,...,Last) and we want to distribute for exemple 50 points in an exponential or other way to make graphs.
so here is my question how to give these 50 points to the players with the rules that the first must have the most of points and the last must not have points.
I'm open for suggestions,
and thank all of you who can help me
You can simply remap a priority.
You were asking the last not having any points, try to add one additional player and remove after calculation.
Let's say we want to distribute 50 points over 10 players by descending priority.
$pointsAvailable = 50;
$playerCount = 10;
We set some kind of priority.
$players = range(1, $playerCount);
$players = array_map(function($p) use ($playerCount, $pointsAvailable) {
return ($playerCount / $p);
}, $players);
Now we know we divide the sum of the distribution by the number of points to get the scalar factor.
$pointScalar = $pointsAvailable / array_sum($players);
$players = array_map(function($p) use ($pointScalar) {
return ($pointScalar * $p);
}, $players);
Here you can see the results.
print_r($players);
Array // The points for the players
(
[0] => 17.07085760737
[1] => 8.5354288036851
[2] => 5.6902858691234
[3] => 4.2677144018426
[4] => 3.4141715214741
[5] => 2.8451429345617
[6] => 2.43869394391
[7] => 2.1338572009213
[8] => 1.8967619563745
[9] => 1.707085760737
)
The sum of all points:
print_r(array_sum($players));
50
i'm working on a project that will need to have everything shown with barcodes, so I've generated 7 numbers for EAN8 algorithm and now have to get these 7 numbers seperately, right now i'm using for the generation
$codeint = mt_rand(1000000, 9999999);
and I need to get this 7 numbers each seperately so I can calculate the checksum for EAN8, how can i split this integer to 7 parts, for example
12345678 to
arr[0]=1
arr[1]=2
arr[2]=3
arr[3]=4
arr[4]=5
arr[5]=6
arr[6]=7
any help would be appreciated..
also I think that I'm becoming crazy :D because I already tried most of the solutions you gave me here before and something is not working like it should work, for example:
$codeint = mt_rand(1000000, 9999999);
echo $codeint."c</br>";
echo $codeint[1];
echo $codeint[2];
echo $codeint[3];
gives me :
9082573c
empty row
empty row
empty row
solved! $codeint = (string)(mt_rand(1000000, 9999999));
Try to use str_split() function:
$var = 1234567;
print_r(str_split($var));
Result:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
)
There are two ways to do this, one of which is reasonably unique to PHP:
1) In PHP, you can treat an integer value as a string and then index into the individual digits:
$digits = "$codeint";
// access a digit using intval($digits[3])
2) However, the much more elegant way is to use actual integer division and a little knowledge about mathematical identities of digits, namely in a number 123, each place value is composed of ascending powers of 10, i.e.: 1 * 10^2 + 2 * 10^1 + 3 * 10^0.
Consequently, dividing by powers of 10 will permit you to access each digit in turn.
it's basic math you can divide them in loop by 10
12345678 is 8*10^1 + 7*10^2 + 6*10^3...
the other option is cast it to char array and then just get it as char
Edit
After #HamZa DzCyberDeV suggestion
$string = '12345678';
echo "<pre>"; print_r (str_split($string));
But in mind it comes like below but your suggestion is better one.
If you're getting string from your function then you can use below one
$string = '12345678';
$arr = explode(",", chunk_split($string, 1, ','));
$len = count($arr);
unset($arr[$len-1]);
echo "<pre>";
print_r($arr);
and output is
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => 8
)
okay what you can do is
Type cast to string with prefill 0
this is how it works
$sinteger = (string)$integer;
$arrsize = 0 ;
for (i=strlen($sinteger), i == 0 ; i--)
{
arr[$arrsize]=$sinteger[i];
$arrsize++;
}
And then what is left you can prefill with zip.
I am sure you can manage the order reverse or previous. but this is simple approach.
I have a list of courses and the hours they require for students to take them. The courses are as follows:
CON8101 Residential Building/Estimating 16 hrs/w
CON8411 Construction Materials I 4 hrs/w
CON8430 Computers and You 4 hrs/w
MAT8050 Geometry and Trigonometry 4 hrs/w
I have used this RegEx to extract the name of course and the hours each course takes each week. There are more than 4 courses, the 4 are examples above. There can be as many as 50 courses.
$courseHoursRegEx = "/\s[0-9]{1,2}\shrs/w/";
$courseNameRegEx = "/[a-zA-Z]{3}[0-9]{4}[A-Z]{0,1}\s?/[a-zA-Z]{3,40}/";
And applied the following function (not sure if 100% right) to extract the RegEx'd strings. Using $courseLine is the variable I saved the string of each line from a text document that early I have fopened. It keeps track of the total hours that has been extracted from the string.
$courses is an array of check boxes that the user enters in the html section
$totalHours += GetCourseHours($courseLine);
function GetCourseHours($couseLine)
{
if(!preg_match($courseHoursRegEx, $courseLine))
{
return $courseLine;
}
}
function GetCourseName($courseLine)
{
if(!preg_match($courseNameRegEx, $courseLine))
{
return $courseLine;
}
}
I used a foreach loop to output all the selected courses to be sorted out in a table.
foreach($courses as $course)
{
$theCourse = GetCourseName($course);
$theHours = GetCourseHours($course)
}
Edit: output code
for($i = 1; $i <= $courses; ++$i)
{
printf("<tr><td>\$%.2f</td><td>\$%.2f</td></tr>", $theCourse, $theHours);
}
I am not sure how to output what I have into a dynamic table organized by the course name, and hours for each course. I cannot get my page to run, I cannot find any syntax errors, I was afraid it was my logic.
First of all, (after fixing a few minor things within the regexes) you can do all of that in one preg_ call. Here is how:
preg_match_all("~([a-zA-Z]{3}\d{4}[A-Z]{0,1}\s.+)\s(\d{1,2})\shrs/w~", $str, $matches);
$str can either be a multiline string with all rows at once. Or you can pass in a single line at a time. If you pass in all lines at once, $matches will afterward look like this:
Array
(
[0] => Array
(
[0] => CON8101 Residential Building/Estimating 16 hrs/w
[1] => CON8411 Construction Materials I 4 hrs/w
[2] => CON8430 Computers and You 4 hrs/w
[3] => MAT8050 Geometry and Trigonometry 4 hrs/w
)
[1] => Array
(
[0] => CON8101 Residential Building/Estimating
[1] => CON8411 Construction Materials I
[2] => CON8430 Computers and You
[3] => MAT8050 Geometry and Trigonometry
)
[2] => Array
(
[0] => 16
[1] => 4
[2] => 4
[3] => 4
)
)
Now you can simply iterate over all names in $matches[1] and sum up the hours in $matches[2]. Notice that those two inner arrays correspond to what's inside of the round brackets I used in the regex. These are so called subpatterns, and they capture additional (sub-)matches. Also $matches[0] will always contain the full match of the whole pattern, but you don't need that in this case.
The problem that I face is in what way if there is issue like the example below:
Codes 1000, 2000, 3000, 4000, 5000
ID 1, 2, 3
========================================
This:
ID number 1 has codes 1000, 2000, 3000, 4000
ID number 2 has codes 2000, 4000, 3000
ID number 3 has codes 3000, 4000, 5000
========================================
When all the fields are connected, each ID has found the same codes.
From the example above, I want to produce fair result and adjusted to the code that it had before on each ID as below: (producing fair codes over the set of ID's)
========================================
To be:
ID number 1 has codes 1000, 2000 (1000 must be on number 1 cause only it has than other)
ID number 2 has codes 3000, 4000
ID number 3 has codes 5000 (5000 must be on number 3 cause only it has than other)
========================================
Some say using Round Robin, but I never heard Round Robin before and I don't have idea how to use it, such a blank mind.
Is there another easier way like to use PHP may be? I'm lost.
Thanks.
=============================================================
Explanation:
I'm making an application where each user has a predefined code and does not have the same code. For example user A has a range of codes between 1000-1500, the user B has a range of codes between 1600 to 2000. And user C has a range of codes between 1300-1550. As we see, the distance of a code on the C contained in the codes on the A (A -> 1000-1500, C -> 1300-1550), will certainly get duplicate between the two user.
With this condition, how to separate and divide it to make it more fair. Let C has 1300, A has 1301, C has 1302 et cetera until 1500.
I thought the simple example I gave before could quite understand, but it seemed like a mess, my mistake.
$codes = array(1000, 2000, 3000, 4000, 5000);
// set up the receiving "containers"
$ids = array(
array(),
array(),
array(),
);
$n_ids = count($ids);
$i = 0;
foreach ($codes as $code) {
// use ($i % $n_ids) to distribute over $n_ids containers
$ids[$i % $n_ids][] = $code;
++$i;
}
print_r($ids);
Output:
Array
(
[0] => Array
(
[0] => 1000
[1] => 4000
)
[1] => Array
(
[0] => 2000
[1] => 5000
)
[2] => Array
(
[0] => 3000
)
)
This problem is a simple distribution task: Distribute N items over M containers.
For every i (0 <= i < N) you select a container to put item N[i] in; the selection is done by using this expression: i mod M (i modulo M).
This expression is what you could call the round-robin, because it goes round like this:
i : 0 1 2 3 4
i % M: 0 1 2 0 1
Even faster
The array_chunk function does this task as well, but I figured you would like to understand the problem first. Also, array_chunk produces a somewhat different result.
$ids = array_chunk(array(1000, 2000, 3000, 4000, 5000), round(count($codes) / 3));
Output:
Array
(
[0] => Array
(
[0] => 1000
[1] => 2000
)
[1] => Array
(
[0] => 3000
[1] => 4000
)
[2] => Array
(
[0] => 5000
)
)
In this question I got help to write a PHP function which gives a pyramid-like distribution:
function getRandomStrength($min, $max) {
$ln_low = log($min, M_E);
$ln_high = log($max, M_E);
$scale = $ln_high-$ln_low;
$rand = (mt_rand()/mt_getrandmax())*$scale+$ln_low;
$value = round(pow(M_E, $rand), 1);
return $value;
}
getRandomStrenth(1.1, 9.9);
// output could be: 1.4 or 8.3 or 9.8 or 7.2 or 2.9 or ...
When I run 50,000 iterations and check how often the numbers from 1 to 9 appear, I get the following list:
1 » 26%
2 » 19%
3 » 14%
4 » 10%
5 » 9%
6 » 7%
7 » 6%
8 » 6%
9 » 4%
This is what I wanted to have. But now I would like to adjust this function a bit. The smaller values should appear more often and the big values should appear less often - so that I get a list like this:
1 » 28%
2 » 20%
3 » 15%
4 » 11%
5 » 9%
6 » 6%
7 » 5%
8 » 5%
9 » 2%
As you can see, I just need a slight modification. But what can I change so that my function behaves as expected?
I tried several things (e.g. changing the base of the logarithm) but this did not change anything.
You can use pow on the random number.
$rand = pow( mt_rand()/mt_getrandmax(), 1.2 )*$scale+$ln_low;
By playing with the exponent value, you can get less or more small value.
Reducing the $scale of your function by a small (constant) amount seems to generate results pretty close to what you're looking for. You can achieve more accurate results by making this reduction of $scale a function of the randomly generated number from mt_rand(), which would require saving (mt_rand()/mt_getrandmax()) to a variable and performing some additional math on $scale.
Here are my tests, you can run it yourself: http://codepad.viper-7.com/ssblbQ
function getRandomStrength($min, $max)
{
$ln_low = log($min, M_E);
$ln_high = log($max, M_E);
$scale = $ln_high-$ln_low - .05; // Subtract a small constant, vary between .05 and .08
$rand = (mt_rand()/mt_getrandmax())*$scale+$ln_low;
$value = round(pow(M_E, $rand), 1);
return $value;
}
$values = array_fill(1, 9, 0);
for( $i = 0; $i < 50000; $i++)
{
$values[ intval( getRandomStrength(1.1, 9.9)) ]++;
}
for( $i = 1; $i <= 9; $i++)
{
$values[ $i] /= 500; // / 50000 * 100 to get a percent
}
var_dump( $values);
Output
Run #1 - Constant = 0.5
array(9) {
[1] => float(26.626) // Should be 28
[2] => float(19.464) // Should be 20
[3] => float(13.476) // Should be 15
[4] => float(10.41) // Should be 11
[5] => float(8.616) // Should be 9
[6] => float(7.198) // Should be 6
[7] => float(6.258) // Should be 5
[8] => float(5.52) // Should be 5
[9] => float(2.432) // Should be 2
}
Run #2 - Constant = 0.65
array(9) {
[1] => float(26.75) // Should be 28
[2] => float(19.466) // Should be 20
[3] => float(13.872) // Should be 15
[4] => float(10.562) // Should be 11
[5] => float(8.466) // Should be 9
[6] => float(7.222) // Should be 6
[7] => float(6.454) // Should be 5
[8] => float(5.554) // Should be 5
[9] => float(1.654) // Should be 2
}
Run #3 - Constant = 0.70
array(9) {
[1] => float(26.848) // Should be 28
[2] => float(19.476) // Should be 20
[3] => float(13.808) // Should be 15
[4] => float(10.764) // Should be 11
[5] => float(8.67) // Should be 9
[6] => float(7.148) // Should be 6
[7] => float(6.264) // Should be 5
[8] => float(5.576) // Should be 5
[9] => float(1.446) // Should be 2
}
For n in {0..1}, y=(x^n)-1, y will range from 0 to x-1. That curve is then easily mapped from 0 to some max value by multiplying by the range and dividing by (x-1). If you change the value x to something near one, the curve will be nearly linear, and at large values, the curve becomes more like a hockey-stick, but will still fall in the same range.
My initial sample value of three won't be precisely what you expressed, but you can adjust it to get the distribution curve you're looking for.
function getCustomStrength($min, $max, $x_val, $base) {
$logmax = $base-1;
$range = $max-$min;
return (pow($base,$x_val)-1)*($range/($base-1))+$min;
}
function getRandomStrength($min, $max) {
$rand = mt_rand()/mt_getrandmax();
$base = 3.0;
return getCustomStrength($min, $max, $rand, $base);
}
getRandomStrength(1.1, 9.9);