title: Algorithm for probability when looping over a randomly ordered array
body: <p>The problem is pretty straightforward, I think, by looking at the code.  I have a randomized array (the array <strong>must</strong> 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 <code>$rules</code>) 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) </p>
  
  <p>Consider the code:</p>
  
  <pre><code>&lt;?php
    // Taken from php.net/shuffle user notes
    // Shuffles an array order for the sake of foreach while maintaining
    // key =&gt; value associations
    function shuffle_assoc(&amp;$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' =&gt; 20,
      'white' =&gt; 10,
      'red' =&gt; 40,
      'green' =&gt; 5,
      'blue' =&gt; 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' =&gt; 0);
    foreach($rules as $k =&gt; $v) { 
      $scores[$k] = 0;
    }
  
    $count = count($rules);
  
    for($x = 0; $x &lt; $i; $x++) { 
      $rules = shuffle_assoc($rules);
  
      foreach($rules as $k =&gt; $probability) {
        $rand = mt_rand(1,100);
        //$probability = ??; I've tried applying many different operations here to "correct" the probability
  
        if($rand &gt; $probability) { 
          continue; 
        } else {
          $scores[$k]++;
          continue 2;
        }
      }
      $scores['outs']++;
    }
  
  
    foreach($scores as $k =&gt; $v) { 
      echo "$k: " . (($v/$i)*100) . "% ($v/$i)\n";
    }
  
  ?&gt;
  </code></pre>
  
  <p>Expected output (pseudo).  Note the percentages correspond with the values of <code>$rules</code></p>
  
  <pre><code>outs: less than 1% (.../1000000)
  black: 20% (.../1000000)
  white: 10% (.../1000000)
  red: 40% (.../1000000)
  green: 5% (.../1000000)
  blue: 25% (.../1000000)
  </code></pre>
  
  <p>Example output:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>Things I've tried &amp; Considerations:</p>
  
  <ul>
  <li><p>As you can see, within the loop I have a commented out section of <code>$probability = ??</code> which I've tried various obvious-to-me methods of calculating the actual probability to use within each element, including playing with <code>$count</code> (count of rules) which is why that variable exists and isn't used.</p></li>
  <li><p>It doesn't have to be exact obviously, but preferably has stable results over a smaller set of numbers (e.x. 1,000 iterations).</p></li>
  <li><p>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.</p></li>
  <li><p>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).</p></li>
  <li><p>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 <code>$rules</code>) 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.</p></li>
  </ul>
  
  <p>Tell me there is a precise way to calculate this.  It's driving me nuts.</p>
  
  <blockquote>
    <p><strong>Edit:</strong> 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: <a href="http://pastebin.com/eB3TVP1E" rel="nofollow">http://pastebin.com/eB3TVP1E</a></p>
  </blockquote>