Task: Birthday problem

C: #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define DEBUG 0 
#define DAYS 365
#define EXCESS (RAND_MAX / DAYS * DAYS)

int days[DAYS];

inline int rand_day(void)
{
	int n;
	while ((n = rand()) >= EXCESS);
	return n / (EXCESS / DAYS);
}


int simulate1(int p, int n)
{
	memset(days, 0, sizeof(days));

	while (p--)
		if (++days[rand_day()] == n) return 1;

	return 0;
}




double prob(int np, int n, double n_sigmas, double p_thresh, double *std_dev)
{
	double p, d; 
	int runs = 0, yes = 0;
	do {
		yes += simulate1(np, n);
		p = (double) yes / ++runs;
		d = sqrt(p * (1 - p) / runs);
		if (DEBUG > 1)
			printf("\t\t%d: %d %d %g %g        \r", np, yes, runs, p, d);
	} while (runs < 10 || fabs(p - p_thresh) < n_sigmas * d);
	if (DEBUG > 1) putchar('\n');

	*std_dev = d;
	return p;
}


int find_half_chance(int n, double *p, double *dev)
{
	int lo, hi, mid;

reset:
	lo = 0;
	hi = DAYS * (n - 1) + 1;
	do {
		mid = (hi + lo) / 2;

		
		
		
		*p = prob(mid, n, 5, .5, dev);

		if (DEBUG)
			printf("\t%d %d %d %g %g\n", lo, mid, hi, *p, *dev);

		if (*p < .5)	lo = mid + 1;
		else		hi = mid;

		if (hi < lo) {
			
			
			if (DEBUG) puts("\tMade a mess, will redo.");
			goto reset;
		}
	} while (lo < mid || *p < .5);

	return mid;
}

int main(void)
{
	int n, np;
	double p, d;
	srand(time(0));

	for (n = 2; n <= 5; n++) {
		np = find_half_chance(n, &p, &d);
		printf("%d collision: %d people, P = %g +/- %g\n",
			n, np, p, d);
	}

	return 0;
}











Ada: with Ada.Command_Line, Ada.Text_IO, Ada.Numerics.Discrete_random;

procedure Birthday_Test is
   
   Samples: constant Positive := Integer'Value(Ada.Command_Line.Argument(1));
   
   
   
   subtype Day is integer range 0 .. 365; 
   subtype Extended_Day is Integer range 0 .. 365*4; 
   package ANDR is new Ada.Numerics.Discrete_Random(Extended_Day);
   Random_Generator: ANDR.Generator;

   function Random_Day return Day is (ANDR.Random(Random_Generator) / 4);
   
   
   type Checkpoint is record
      Multiplicity:  Positive;
      Person_Count:   Positive;
   end record;
   Checkpoints: constant array(Positive range <>) of Checkpoint 
     := ( (2, 22),  (2, 23),  (3, 86),  (3, 87), (3, 88),  
	  (4, 186), (4, 187), (5, 312), (5, 313), (5, 314) );
   type Result_Type is array(Checkpoints'Range) of Natural;
   Result: Result_Type := (others => 0);
   
   
   
   procedure Experiment(Result: in out Result_Type) is
   
      A_Year: array(Day) of Natural := (others => 0);
      A_Day: Day;
      Multiplicity: Natural := 0;
      People: Positive := 1;
   begin
      for I in Checkpoints'Range loop
	 while People <= Checkpoints(I).Person_Count loop
	    A_Day := Random_Day;
	    A_Year(A_Day) := A_Year(A_Day)+1;
	    if A_Year(A_Day) > Multiplicity then
	       Multiplicity := Multiplicity + 1;
	    end if;
	    People := People + 1;
	 end loop;
	 if Multiplicity >= Checkpoints(I).Multiplicity then
	    Result(I) := Result(I) + 1;
            
	 end if;
      end loop;
   end Experiment;
   
   package TIO renames Ada.Text_IO;
   package FIO is new TIO.Float_IO(Float);

begin
    
    ANDR.Reset(Random_Generator);

    
    for I in 1 .. Samples loop
       Experiment(Result);
    end loop;

    
    TIO.Put_Line("Birthday-Test with" & Integer'Image(Samples) & " samples:");
    for I in Result'Range loop
       FIO.Put(Float(Result(I))/Float(Samples), Fore => 3, Aft => 6, Exp => 0);
       TIO.Put_Line
	 ("% of groups with" & Integer'Image(Checkpoints(I).Person_Count) & 
	  " have"            & Integer'Image(Checkpoints(I).Multiplicity) & 
	  " persons sharing a common birthday.");
    end loop;
end Birthday_Test;