5

I have a map of users and a count of entries/tickets they have purchased for a raffle (or lottery or any other similar event).

The map of users to entries is in the structure as seen below:

// Map the person to the amount of entries they have purchased
$entries = [
    'Adam' => 5,
    'James' => 3,
    'Oliver' => 4,
    'Holly' => 8
];

I want to choose a random user but take into consideration their chance based on ticket count. The probability of winning must be:

(User Ticket Amount / Total Ticket Count) * 100 = Probability percentage

For example, from the test array the expected results is Holly will win the raffle 8 times out of 20 times.

7
  • Do you want the probability returned or a random name? Commented Dec 10, 2019 at 13:41
  • Kindly post some of your code/efforts then only we can help you. Commented Dec 10, 2019 at 13:42
  • Well, in term of probability can you just build an array with all the person (5 adam, 3 james, etc.) and then just use array_rand ? Commented Dec 10, 2019 at 13:44
  • @GrumpysaysReinstateMonica A random name Commented Dec 10, 2019 at 13:48
  • 2
    @MickaëlLeger how would i do that? go over array and then add the user to a new array as many times as they got entries? surely if there are thousands of entries for one user and they have a longer name this can cause parformance issues? Commented Dec 10, 2019 at 13:50

3 Answers 3

8

It's a coincidence you are asking this as I created a method to do just this the other day when writing a script for a betting site.

Please see the comments for explanation of what the code consists of:

// The array passed to the function should be your $entries array
function randProb(array $items) {
  $totalProbability = 0; // This is defined to keep track of the total amount of entries

  foreach ($items as $item => $probability) {
      $totalProbability += $probability;     
  }

  $stopAt = rand(0, $totalProbability); // This picks a random entry to select
  $currentProbability = 0; // The current entry count, when this reaches $stopAt the winner is chosen

  foreach ($items as $item => $probability) { // Go through each possible item
      $currentProbability += $probability; // Add the probability to our $currentProbability tracker
      if ($currentProbability >= $stopAt) { // When we reach the $stopAt variable, we have found our winner
          return $item;
      }
  }

  return null;
}

Any questions feel free to ask below, if you would like to output the value (probability) of the winning item, return $probability at the breakpoint instead of $item

Sign up to request clarification or add additional context in comments.

4 Comments

this is perfect I think this will work... I'm unsure where to put this code though at top of my file?
@AdamZorchubiez I'm glad I could help
@JoshWood this can't be completely random , as if $stopAt is a little number , like 4 and the user with low probability like 7 , ( assume others above 7 ) can win this if hes first in the list . sort of the items matter , so it's not random
does this work both with sorted and unsorted arrays?
1

This should do what you are trying to do, and is pretty simple to understand.

function rand_with_entries($entries) {

    //create a temporary array
    $tmp = [];

    //loop through all names
    foreach($entries as $name => $count) {

        //for each entry for a specific name, add name to `$tmp` array
        for ($x = 1; $x <= $count; $x++) {
            $tmp[] = $name;
        }

    }

    //return random name from `$tmp` array
    return $tmp[array_rand($tmp)];
}

5 Comments

Let's hope there won't be too much people buying too much tickets. RAM will suffer a lot
@Cid I think you would sooner hit your max allowed memory size in many environments before this causes any sort of issues. I tried it on multiple online PHP testers with 300k+ array keys and it spit out the name instantly. On my own personal server it also handled 300k with ease. I would do more, but generating the huge $entries array starts to take some time above 300k
300k numeric or string keys ?
@Cid Strings, I was using md5(rand()) for the keys.
0

Ok so you have this data :

$entries = [
    'Adam' => 5,
    'James' => 3,
    'Oliver' => 4,
    'Holly' => 8
];

One way can be to fill an array according to this data and use array_rand. Here is an example :

function pickRand($array, $nb) {
    $newArray = [];
    foreach ($array as $name => $probability) {
        $num = $probability;
        while ($num > 0) {
            $newArray[] = $name;
            $num--;
        }
    }
    return $newArray[array_rand($newArray, $nb)];
}

And to use it :

$randomName = pickRand($entries, 1);

2 Comments

What is your $nb variable? Amount of returns?
@GrumpysaysReinstateMonica yes, since it's a param in array_rand, better to use it in a function I think !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.