Get number by knowing chance - php

I don't think the article title is correct so I will try to explain what I need.
ATM I have array:
array(
'start' => 1,
'end' => 10,
'lucky_numbers' => 6
);
and knowing this array I can define that chance to win is 60% out of 100%. The next part is the second array:
array(0, 1.25, 0.5, 1.25, 0, 3, 0, 1, 0.5, 1.25, 0.5, 1.25, 0, 2, 0.5, 2)
and this is the hard part. I don't have any clue how to pick one number knowing that chance to pick not a zero is 60%. Any ideas?
EDIT
this is the wheel numbers. when user spins the wheel i need to give him 60% chance to win. So 60% chance to spin not the 0

Let me see if i understand:
-This is a "slots like" winnings multiplier minigame for a game where you roll a "wheel" showing the possibles multipliers the player can win, this wheel is the second array, this array is variable in lenght.
-You want to give the player a variable chance to win (sometimes 60%, sometimes 80%, sometimes 20%).
If you only want to be sure the player doesn't get a "0%" multiplier, do the opposite, take the possibility of a "0%" to appear and put them the equivalent in the array and then fill the array with random multipliers and shuffle it.
$multipliers = [0.5, 1.25, 2, 3];
$wheel = [];
for ($i = 0; $i < $arraylenght; $i++) {
if ($i < floor($arraylenght * (1 - ((float)$luckyNumbers/10)))){
$wheel[] = 0;
} else {
$wheel[] = array_rand($multipliers);
}
}
shuffle($wheel);
Now if you also want to control the probabilities of each multiplier... That's another beast.

Related

Iterate through 2d array of booleans and leave only the largest contiguous "2D blob of ones"

Ok, so the question is kind of awkwardly phrased, but I hope this will clear things up.
I have this sample 2d array.
$array = array(
array(1, 0, 0, 0, 1, 0, 0, 1),
array(0, 0, 1, 1, 1, 1, 0, 1),
array(0, 1, 1, 0, 1, 0, 0, 0),
array(0, 1, 1, 0, 0, 0, 1, 0),
array(1, 0, 0, 0, 1, 1, 1, 1),
array(0, 1, 1, 0, 1, 0, 1, 0),
array(0, 0, 0, 0, 0, 0, 0, 1)
);
When iterated by rows (and terminating each row with \n), and for every row then iterated by column, it will echo something like this: (░░ = 0, ▓▓ = 1)
▓▓░░░░░░▓▓░░░░▓▓
░░░░▓▓▓▓▓▓▓▓░░▓▓
░░▓▓▓▓░░▓▓░░░░░░
░░▓▓▓▓░░░░░░▓▓░░
▓▓░░░░░░▓▓▓▓▓▓▓▓
░░▓▓▓▓░░▓▓░░▓▓░░
░░░░░░░░░░░░░░▓▓
But what I'd like to do is to "analyse" the array and only leave 1 contiguous shape (the one with the most "cells"), in this example, the result would be:
░░░░░░░░▓▓░░░░░░
░░░░▓▓▓▓▓▓▓▓░░░░
░░▓▓▓▓░░▓▓░░░░░░
░░▓▓▓▓░░░░░░░░░░
▓▓░░░░░░░░░░░░░░
░░▓▓▓▓░░░░░░░░░░
░░░░░░░░░░░░░░░░
My initial approach was to:
Assign each ▓▓ cell a unique number (be it completely random, or the current iteration number):
01 02 03
04050607 08
0910 11
1213 14
15 16171819
2021 22 23
24
Iterate through the array many, MANY times: every iteration, each ▓▓ cell assumes the largest unique number among his neighbours. The loop would go on indefinitely until there's no change detected between the current state and the previous state. After the last iteration, the result would be this:
01 21 08
21212121 08
2121 21
2121 24
21 24242424
2121 24 24
24
Now it all comes down to counting the value that occurs the most. Then, iterating once again, to turn all the cells whose value is not the most popular one, to 0, giving me the desired result.
However, I feel it's quite a roundabout and computationally heavy approach for such a simple task and there has to be a better way. Any ideas would be greatly appreciated, cheers!
BONUS POINTS: Divide all the blobs into an array of 2D arrays, ordered by number of cells, so we can do something with the smallest blob, too
Always fun, these problems. And done before, so I'll dump my code here, maybe you can use some of it. This basically follows every shape by looking at a cell and its surrounding 8 cells, and if they connect go to the connecting cell, look again and so on...
<?php
$shape_nr=1;
$ln_max=count($array);
$cl_max=count($array[0]);
$done=[];
//LOOP ALL CELLS, GIVE 1's unique number
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
if($array[$ln][$cl]===0)continue;
$array[$ln][$cl] = ++$shape_nr;
}}
//DETECT SHAPES
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
if($array[$ln][$cl]===0)continue;
$shape_nr=$array[$ln][$cl];
if(in_array($shape_nr,$done))continue;
look_around($ln,$cl,$ln_max,$cl_max,$shape_nr,$array);
//SET SHAPE_NR to DONE, no need to look at that number again
$done[]=$shape_nr;
}}
//LOOP THE ARRAY and COUNT SHAPENUMBERS
$res=array();
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
if($array[$ln][$cl]===0)continue;
if(!isset($res[$array[$ln][$cl]]))$res[$array[$ln][$cl]]=1;
else $res[$array[$ln][$cl]]++;
}}
//get largest shape
$max = max($res);
$shape_value_max = array_search ($max, $res);
//get smallest shape
$min = min($res);
$shape_value_min = array_search ($min, $res);
// recursive function: detect connecting cells
function look_around($ln,$cl,$ln_max,$cl_max,$nr,&$array){
//create mini array
$mini=mini($ln,$cl,$ln_max,$cl_max);
if($mini===false)return false;
//loop surrounding cells
foreach($mini as $v){
if($array[$v[0]][$v[1]]===0){continue;}
if($array[$v[0]][$v[1]]!==$nr){
// set shape_nr of connecting cell
$array[$v[0]][$v[1]]=$nr;
// follow the shape
look_around($v[0],$v[1],$ln_max,$cl_max,$nr,$array);
}
}
return $nr;
}
// CREATE ARRAY WITH THE 9 SURROUNDING CELLS
function mini($ln,$cl,$ln_max,$cl_max){
$look=[];
$mini=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];
foreach($mini as $v){
if( $ln + $v[0] >= 0 &&
$ln + $v[0] < $ln_max &&
$cl + $v[1] >= 0 &&
$cl + $v[1] < $cl_max
){
$look[]=[$ln + $v[0], $cl + $v[1]];
}
}
if(count($look)===0){return false;}
return $look;
}
Here's a fiddle
I can only think of a few minor improvements:
Keep a linked list of the not empty fields. In step 2 you do not need to touch n² matrix-elements, you only need to touch the ones in your linked list. Which might be much less depending how sparse your matrix is.
You only need to compare to the right, right-down, left-down and down directions. Otherwise The other directions are already checked from the former row/column. What I mean: When I am greater that my right neighbour, I can already change the number of the right neighbour. (same for down and right-down). This halfs the number of compairs.
If your array size isn't huge and memory won't be a problem maybe a recursive solution would be faster. I found a c++ algorithm that does this here:
https://www.geeksforgeeks.org/find-length-largest-region-boolean-matrix/

PHP array product combinations

I have produced an array of prime factors of a number - this is supposed to be the hard part! However, in order to create a list of the divisors of the same number the prime factors need to be combined in every which possible way. Something that I'm struggling to do with php.
For example I have an array of:
2
2
2
3
3
41
53
...for the number 156456; multiply them all together and you get back to the number. What I need to do is to multiply all of the duos together e.g. 2x2, 2x3, 2x53 etc and then all of the triplets together and so on until I finally multiply the 7 blocks of six together.
As you can see this will give a very large array with all of the divisors in, 4, 6, 8, 9, 12 etc. with many duplicates. I just can't seem to get from the array I have above to this array of divisors that I want. It's a case of multiplying out every possible combination of elements in the array, is there a php function for this, my search so far has been fruitless?
After reading this page: http://mathcentral.uregina.ca/QQ/database/QQ.02.06/joe1.html, I tried to build something that might work, It may not be the most efficient solution and its also limited to count($primes) <= 32 on 32 bit systems. If you need more, feel free to use a Bitset:
$primes = Array(2, 2, 2, 3, 3, 41, 53);
$num_primes = count($primes); // 7, if this is over 32, it won't work on 32bit systems
$divisors = Array();
// number of possible combinations
$limit = pow(2, $num_primes) - 1; // 127
// count a number up and use the binary
// representation to say which index is
// part of the current divisor
for($number = 0; $number <= $limit; $number++) {
$divisor = 1;
// only multiply activated bits in $number to the divisor
for($i = 0; $i < $num_primes; $i++) {
$divisor *= ($number >> $i) & 1 ? $primes[$i] : 1;
}
$divisors[] = $divisor;
}
echo implode(", ", array_unique($divisors));
This results into the following divisors:
1, 2, 4, 8, 3, 6, 12, 24, 9, 18, 36, 72, 41, 82, 164, 328, 123, 246, 492,
984, 369, 738, 1476, 2952, 53, 106, 212, 424, 159, 318, 636, 1272, 477,
954, 1908, 3816, 2173, 4346, 8692, 17384, 6519, 13038, 26076, 52152, 19557,
39114, 78228, 156456
To find all divisors you need to multiply each prime factor with each other in every possible combination. To do this I calculate the number of possible combinations ($limit). If you now count a number up to this limit the binary representation looks something like this:
7 bit
<----->
0000000 0
0000001 1
0000010 2
0000011 3
0000100 4
0000101 5
0000110 6
0000111 7
0001000 8
0001001 9
...
1111110 126
1111111 127
The current binary representation of $number represents which indexes of $primes are used to calculate the current $divisor. To show this better let's say $number = 5, which is 0000101 in binary. And the calculation for $divisor would be 2 * 1 * 2 * 1 * 1 * 1 * 1 = 4. Only the first and the third bit is set, so only the first and the third element in the array is used for the calculation.
I hope this makes it a little bit clearer.

Algorithm for probability when looping over a randomly ordered array

The problem is pretty straightforward, I think, by looking at the code. I have a randomized array (the array must be randomized, some code has been excluded because it doesn't pertain to the actual problem, but does require randomization). For each element in the array, there is a "probability" index (described here as the value itself, in $rules) that is suppose to hint that, if other conditions are met (that are removed here for the sake of non-relevancy), the probability that array element will be "triggered" (in this case, that the array element's score will increment by 1)
Consider the code:
<?php
// Taken from php.net/shuffle user notes
// Shuffles an array order for the sake of foreach while maintaining
// key => value associations
function shuffle_assoc(&$array) {
$keys = array_keys($array);
shuffle($keys);
foreach($keys as $key) {
$new[$key] = $array[$key];
}
return $new;
}
$i = 1000000; // How many tests to perform
// This is my rule list. Each key is a simple color
// and each value is a probability represented as a percent
$rules = array(
'black' => 20,
'white' => 10,
'red' => 40,
'green' => 5,
'blue' => 25,
);
// Initialize the scores array with all 0's
// The "outs" will be used when the probability does not
// occur in any of the rules
$scores = array('outs' => 0);
foreach($rules as $k => $v) {
$scores[$k] = 0;
}
$count = count($rules);
for($x = 0; $x < $i; $x++) {
$rules = shuffle_assoc($rules);
foreach($rules as $k => $probability) {
$rand = mt_rand(1,100);
//$probability = ??; I've tried applying many different operations here to "correct" the probability
if($rand > $probability) {
continue;
} else {
$scores[$k]++;
continue 2;
}
}
$scores['outs']++;
}
foreach($scores as $k => $v) {
echo "$k: " . (($v/$i)*100) . "% ($v/$i)\n";
}
?>
Expected output (pseudo). Note the percentages correspond with the values of $rules
outs: less than 1% (.../1000000)
black: 20% (.../1000000)
white: 10% (.../1000000)
red: 40% (.../1000000)
green: 5% (.../1000000)
blue: 25% (.../1000000)
Example output:
outs: 30.7128% (307128/1000000)
black: 13.2114% (132114/1000000)
white: 6.3381% (63381/1000000)
red: 29.5247% (295247/1000000)
green: 3.1585% (31585/1000000)
blue: 17.0545% (170545/1000000)
Things I've tried & Considerations:
As you can see, within the loop I have a commented out section of $probability = ?? which I've tried various obvious-to-me methods of calculating the actual probability to use within each element, including playing with $count (count of rules) which is why that variable exists and isn't used.
It doesn't have to be exact obviously, but preferably has stable results over a smaller set of numbers (e.x. 1,000 iterations).
It can be pretty fuzzy. A variance of +/- 5% wouldn't hurt my feelings, especially in smaller numbers of iterations, I understand big number theory comes to play here.
The number of outs isn't a big deal as long as they're less than 1%-2%. I also tried eliminating outs using various methods to see if the outs alone were skewing, and interestingly enough when I did that on one occasion, I got a 20% split all around (i.e. even).
Furthermore, on "outs", I was able to get pretty close to the proper split with very little outs by basically brute-forcing the probability "numbers" (that is, the values of $rules) starting from 100 backwards, but I was never able to find out a precise, optimal method. Each time, I would get closer to the result for one color, that would skew the other colors on a small but noticeable scale. There was no easy-for-me-to-grasp correlation in these numbers and were seemingly random although it is obvious that the results played well with probability vs big numbers.
Tell me there is a precise way to calculate this. It's driving me nuts.
Edit: I have a finalized version of my code, with the help from the two answers below, that does this without the need for knowing probability percentages before the loop begins, and no additional or nested loops (which is what I specifically needed, I guess I should of been more direct in that part) .. In the sense of, each iteration, you could be pulling the probability dynamically based on that specific iteration's properties.. All answers here were invaluable, here is my version of the final code: http://pastebin.com/eB3TVP1E
Just normalize the results, accumulate them and then you are done.
What I mean is:
sum all probabilities given for every item of the array to get the total (which is 100 in your case but it's easily generalizable)
divide every probability for the total
So for example:
$rules = array(
'black' => 20,
'white' => 10,
'red' => 40,
'green' => 5,
'blue' => 25,
);
will be normalized to:
$rules_norm = array(
'black' => 0.2,
'white' => 0.1,
'red' => 0.4,
'green' => 0.05,
'blue' => 0.25,
);
now accumulate the result so that for every element in $rules_norm you calculate the sum of all previous elements plus the current one.
So:
$rules_norm = array(
'black' => 0.2,
'white' => 0.3,
'red' => 0.7,
'green' => 0.75,
'blue' => 1.0,
);
Now with this you can just extract a random float number in range [0,1) and choose which elements are increased according to the result: to increment the score of one element just start from the first one in the array and increment the one such that $rand > $rules_norm[k]
Jack's idea implemented in your code (if the sum of probabilities is >100 this won't work):
php fiddle
<?php
// Taken from php.net/shuffle user notes
// Shuffles an array order for the sake of foreach while maintaining
// key => value associations
function shuffle_assoc(&$array) {
$keys = array_keys($array);
shuffle($keys);
foreach($keys as $key) {
$new[$key] = $array[$key];
}
return $new;
}
$i = 1000000; // How many tests to perform
// This is my rule list. Each key is a simple color
// and each value is a probability represented as a percent
$rules = array(
'black' => 20,
'white' => 10,
'red' => 40,
'green' => 5,
'blue' => 25,
);
// Initialize the scores array with all 0's
// The "outs" will be used when the probability does not
// occur in any of the rules
$scores = array('outs' => 0);
foreach($rules as $k => $v) {
$scores[$k] = 0;
}
$count = count($rules);
//$limits is what Jack called $rules_norm
$limits=array();
$limit=0;
foreach($rules as $k=>$v)
{
$limit+=$v;
$limits[$k]=$limit;
}
for($x = 0; $x < $i; $x++) {
$rand = mt_rand(1,100);
foreach($limits as $k=>$v)
{
if($v>=$rand)
{
$scores[$k]++;
continue(2);
}
}
$scores['outs']++;
}
foreach($scores as $k => $v) {
echo "$k: " . (($v/$i)*100) . "% ($v/$i)\n";
}
?>

How to pick random numbers from pot with some winning numbers?

i am trying to make a very simple game where i have 7 positions which are all hidden and within those there a 3 winning positions. I can pick randomly 3 times. I need to display whether the pick is a winning or not after every pick and store the result in a base.
Currently my thought where to generate an array of winning numbers on the first pick and then pick random number and check if it is in the winning array.
But i have a feeling that there is much more efficient way to do so.
Would appreciate if you would use PHP for coding examples, but pseudo code will do as-well.
EDIT
i am looking for the way to solve this without populating array with winning positions. maybe there is a way to do this with weights or probability percents.
Something like on first pick i have 3/7*100 percent chance to win. save result to base.
on second pick i have either 3/6*100 or 2/6*100 percent chance to win based weather i won in previous pick which i get from base.
Revised answer: this example does not require you to store the complete state of the game in a variable; instead, you just need to store the try count and won count:
$won = 0;
for($try = 0; $try < 3; $try++) {
$slots = array_fill(0, 7 - $try, 0); // blank slots
$lucky = array_fill(0, 3 - $won, 1); // lucky slots
$pot = array_replace($slots, $lucky); // make some of the slots lucky
$win = $pot[array_rand($pot)]; // randomly pick a slot
$won += $win == 1; // update won count
echo sprintf("Try %d: win=%d, total wins=%d\n", $try + 1, $win, $won);
}
Original answer:
$pot = array( // pot is (an associative) array; 0 = blank, 1 = win
"pos_1" => 0,
"pos_2" => 0,
"pos_3" => 0,
"pos_4" => 0,
"pos_5" => 0,
"pos_6" => 0,
"pos_7" => 0
);
$win = array_rand($pot, 3); // picks three indexes from the pot randomly
foreach($win as $w) {
$pot[$w] = 1; // set winning indicator
}
print_r($pot);
Output: array containing state of the pots.
Array
(
[pos_1] => 0
[pos_2] => 1
[pos_3] => 0
[pos_4] => 1
[pos_5] => 1
[pos_6] => 0
[pos_7] => 0
)
You can just save the positions of the winning numbers. This way you can always check their values using the [] operator for arrays. After all, you just pick the positions and not the numbers.
Update:
This way you even don't need to hide numbers. It's quite possible to have some more abstract "winning things" - characters, words, structures. However, it is important that you do not alter your array of hidden "things" in any way or at least update the stored winning positions accordingly if they stay the same. If that's not the case you'd naturally need to update the saved winning positions.
<?php
$arr = array(true, true, false, false, false, false, false);
shuffle($arr);
function pick($arr, $index) {
return isset($arr[$index]) && $arr[$index] === true;
}
var_dump($arr);
var_dump(pick($arr, 3));
var_dump(pick($arr, 5));
var_dump(pick($arr, 1));

Generating a random number but with a twist

I need to generate a random number.
But the twist is that the % of lower numbers should be greater than the higher.
For example.
rand > 1 to 100
13,
15,
12,
16,
87,
15,
27,
12,
1,
12,
98,
12,
53,
12,
14....
The bold integers will be the ones return from the 1-100 range.
The math should be like so rand = a number lower than max/2
Hope you guys can help.
Ps, How would a matrix come into this ? im not superior at maths :(
The abs answer seems to be the one.
$up = $down = 0;
while(true)
{
if(abs((rand()%150)-50) < 50)
{
$up++;
}else
{
$down++;
}
if( ($up + $down) == 500){ break;}
}
echo $up . '/' . $down;
how about
n = abs((rand()%150)-50)
$x = rand(0,1) ? rand(1,100) : rand(1,50);
Simple method: the first rand(0,1) selects between the two cases. Either 1..50 or 1..100 as random range. Since 1,100 already encompases 1,50, the latter range is selected 100% of the time, the former case only in 1 of 2 runs.
If you want a distribution where the highest numer 99 gets selected almost never, but the lower numbers 1..20 pretty frequent, then a simple rand(1,rand(1,100)) would do.
$rand = (rand() * rand()) / (getrandmax() * getrandmax());
This will give you a random number between 0 and 1 with a high probability of falling into the lower end of the range and the probability of a larger number decreasing exponentially. You can then scale this result to any range that you want.

Categories