Search code examples
javascriptprobability

Birthday problem code returns 69.32% instead of 50.05%


I am trying to write a code for the birthday problem. For example, given a group of 23 people, 2 people having the same birthday should be greater than 50%. But for some reason, my code is returning 69.32% which doesn't make sense.

function doProbability() {

  const cntPeople = document.getElementById( 'setSize'    ).valueAsNumber;
  const cntSubset = document.getElementById( 'subsetSize' ).valueAsNumber;
    
  const prob = getProbability( cntPeople, cntSubset );

  const probAsPercentage = prob.toLocaleString( undefined, { style: 'percent', minimumFractionDigits: 2 } );        

  document.getElementById( 'result' ).textContent = "Probability: " + probAsPercentage;
}
    
function getProbability( cntPeople, cntSubset ) {
  
  // Count of number of ways of choosing these pairs (cntPeople choose cntSubset)
  const selectingCnt = binomial( cntPeople, cntSubset );
  
  // Probability of one k subset of people having the same birthday
  const birthdayP = 1.0 / Math.pow( 365, cntSubset - 1 );
  
  // Probability of any k subset of people having the same birthday
  return Math.min( selectingCnt * birthdayP, 1 );
}

function binomial(n, k) {

  // Preconditions:

  if( typeof n !== 'number' || isNaN( n ) || n < 0 ) throw new Error( 'n is invalid' );
  if( typeof k !== 'number' || isNaN( k ) || k < 0 ) throw new Error( 'k is invalid' );
  if( k > n ) throw new Error( 'k cannot be greater than n' );

  //

  let b = 1;

  for ( var t = 0, _pj_a = Math.min(k, n - k); t < _pj_a; t += 1 ) {
    b *= n;
    b /= t + 1;
    n -= 1;
  }

  return b;
}
label { display: block; }
<label>
  Size of entire set of people
  <input type="number" min="1" max="999" value="23" id="setSize" />
</label>

<label>
  Size of small subset of people
  <input type="number" min="1" max="999" value="2" id="subsetSize" />
</label>

<button onclick="doProbability()">Click me</button>

<br />

<output id="result"></output>


Solution

  • Updated your getProbability function based on this working example (https://jsfiddle.net/aM3z/36z0f4pu/).

    Also updated the binomial function by simplifying it.

    function doProbability() {
    
      const cntPeople = document.getElementById( 'setSize'    ).valueAsNumber;
      const cntSubset = document.getElementById( 'subsetSize' ).valueAsNumber;
        
      const prob = getProbability( cntPeople, cntSubset );
    
      const probAsPercentage = prob.toLocaleString( undefined, { style: 'percent', minimumFractionDigits: 2 } );        
    
      document.getElementById( 'result' ).textContent = "Probability: " + probAsPercentage;
    }
        
    function getProbability( cntPeople, cntSubset ) {
      let days = 365
      // Count of number of ways of choosing these pairs (cntPeople choose cntSubset)
      const selectingCnt = binomial( cntPeople, cntSubset );
      
      const birthdayP = (days - 1) / days
      
      return ((1 - Math.pow(birthdayP, selectingCnt)) * 100).toFixed(2);
    }
    
    function binomial(n, k) {
    
      // Preconditions:
    
      if( typeof n !== 'number' || isNaN( n ) || n < 0 ) throw new Error( 'n is invalid' );
      if( typeof k !== 'number' || isNaN( k ) || k < 0 ) throw new Error( 'k is invalid' );
      if( k > n ) throw new Error( 'k cannot be greater than n' );
    
      return (n * (n - 1)) / k;
    }
    label { display: block; }
    <label>
      Size of entire set of people
      <input type="number" min="1" max="999" value="23" id="setSize" />
    </label>
    
    <label>
      Size of small subset of people
      <input type="number" min="1" max="999" value="2" id="subsetSize" />
    </label>
    
    <button onclick="doProbability()">Click me</button>
    
    <br />
    
    <output id="result"></output>