Concepts / The Birthday Problem

The Birthday Problem

Sequential access processes array elements one after another in array order, while random access lets you reach any element directly at any time.

  • Programming

Waiting for a Shared Birthday

Suppose you walk into a room and start asking people for their birthdays. How long do you expect to wait before someone shares a birthday with a person you already asked? Most people give an answer that is much larger than what actually happens. This gap between expectation and reality is the starting point for this article. It also shows one of the most useful properties of arrays, random access, and how a simple array lets a program answer the question quickly.

Keep one question in mind as you read: when a program wants the value at one particular position, how does it get there? The answer depends on whether the access is direct, and the rest of this article explains what that means and why it matters.

Two Ways to Visit an Array

Up to this point, the array examples in the course have used sequential access. In sequential access, the elements of an array are processed one after another, in the order they appear. You start at the first position, move to the second, then the third, and so on until you finish. Many useful tasks look like this, such as totaling all the values in a list or scanning for the largest one.

Sequential access describes how the work is ordered, not a limit on what the array can do. When a problem asks you to visit elements in order, sequential processing is the natural fit. The birthday problem does not fit that pattern, and that is where random access becomes important.

Random access means that every element of the array is equally accessible at any given time. Your program does not need to walk through the earlier positions to reach a later one. If you want the element at a particular position, you can go straight to it, whether that position is the first, the middle, or the last.

AspectSequential accessRandom access
Order of visitsPosition by position, in array orderAny position, in any order
How a position is reachedPassing through the earlier positions firstNaming the position directly
Typical useTotaling values, scanning for the largestLooking up a slot the next step could need, anywhere in the array
Cost of reaching a later positionMust walk past the earlier onesSame single direct step as any other position

Sequential access versus random access to array elements

Index 0visited firstIndex 3named directlyIndex 1visited secondIndex 0not needed for this lookupIndex 2visited thirdIndex 3reached after passing 0 to2nextnextnext
How do sequential and random access differ in the order elements are visited and in how a position is reached?

Why Every Slot Is Equally Reachable

An array holds a group of values, and each value sits at a numbered position. Positions label the slots, while the values are the contents stored in those slots. The word direct carries the main idea: an array element is identified by its position, so the program can jump straight to the position it needs. Whether that position is first, last, or in the middle makes no difference to how the program reaches it.

This is why arrays are used whenever a program must revisit a specific slot again and again. The source presents this as the defining ability of arrays. Because no slot is closer or more reachable than another, the cost of reaching index 4 is the same as the cost of reaching index 0.

The short Java program below is an original illustration, not the source file. It shows the core move: naming a position in brackets returns the value stored there. The program does not loop through the array to find the value.

java
Output
false
true
Notes on the code
  • new boolean[5] creates five slots with positions 0 to 4
  • used[2] names position 2 directly, without touching used[0] or used[1]
  • Assigning used[2] = true changes only that one slot

Changing the number inside the brackets changes which element is returned, and the program reaches any of them in the same single step. This is the behavior the birthday problem relies on when it looks up a day.

Tracing Index 3 Without Visiting Earlier Slots

The source uses a five-position diagram, which is a small stand-in for the 365-slot array the birthday problem needs. Each cell can be reached by its index. To check day number 2, the program names used[2] and reads it. It does not need to inspect used[0] or used[1] first. Those two cells hold true and false respectively, but they have no bearing on the lookup of position 2.

used[0]true, not readused[1]false, not readused[2]read directlyused[3]not readused[4]not read
How does the program jump straight to the element at index 2 without visiting index 0 or index 1 first?
Worked example

Looking up day 2 in a five-slot array

The program needs to know whether day 2 has been marked, using the five-slot illustration.

  1. 1
    Name the position

    The program writes used[2], which identifies slot 2 by its index.

  2. 2
    Go straight there

    The request goes to slot 2. The program does not pass through slot 0 or slot 1.

  3. 3
    Read the value

    The value stored in that slot is returned. Its neighbors, which hold true and false, do not affect this lookup.

Result

The program has the value at position 2 in a single step, regardless of where that position sits in the array.

What the Birthday Simulation Remembers

To simulate the birthday experiment, the program must remember which birthdays it has already seen. There are 365 possible birthdays, and the setup ignores leap years. For each possible birthday, the program needs one piece of information: has someone with this birthday already been found? The answer is a boolean value. Gathering 365 boolean answers into one structure is exactly what an array is for.

Each slot corresponds to one day number. The value in used[i] is true if someone whose birthday is day number i has already been selected, and false otherwise. The index does the work of identifying the day. The program never searches the array for a matching birthday. It jumps straight to the slot that matches the birthday it just drew.

New boolean arrays start with false in every slot, so unseen birthdays need no setup before they are checked. The same idea explains why the array is initialized with false values: every day is ready to be checked the moment the simulation begins.

What do you think happens?

The array is created fresh and no birthdays have been recorded yet. What does used[17] hold before the simulation picks anyone?

  • true

  • false

  • a random day number

  • nothing, because the slot does not exist yet

Reveal answer
Answer:

false

A new boolean array starts with false in every slot, so the slot for day 17 holds false, meaning that birthday has not been seen.

Recording and Checking a Drawn Birthday

Each time the simulation picks a person, the birthday drawn could be any day from 0 to 364. The next birthday has no relationship to the previous one, so the program cannot know in advance which slot it will need. A purely sequential scan would waste effort walking past slots that have nothing to do with the new birthday. Random access solves this: the program computes the day number, reads used at that index, and either stops or records the day as used.

Draw a random dayany day from 0 to 364Read used[day]direct index lookupAlready true?was this day seen beforeStop: shared birthdayfounda repeatSet used[day] to truerecord the day as seenday numbervalue readyesno
What happens step by step when the simulation picks a random day, reads that index, and checks whether it was already marked?
  1. 1
    Draw a random day number from 0 to 364.
  2. 2
    Name that day as the index and read used at that index in one direct step.
  3. 3
    If the value is true, someone with this birthday has already been selected, so the simulation stops.
  4. 4
    If the value is false, set used at that index to true to record that the day has now been seen.
  5. 5
    Draw the next person's birthday and repeat, with no scan of the other slots.

Mistakes About Reaching Array Elements

  • Common mistake

    Assuming you must start at the front of the array and step forward to reach a later element.

    What it looks like

    Believing that to read used[3] the program has to check used[0], then used[1], then used[2].

    Why it fails

    Random access means every element is equally accessible at any time. The index names the position directly, so the earlier slots are not visited.

    Do this instead

    Name the index you need, such as used[3], and read that slot in one step.

  • Common mistake

    Confusing the position label with the value stored there.

    What it looks like

    Treating the 2 in used[2] as the value held in that slot.

    Why it fails

    Positions label the slots, while the values are the contents stored in those slots.

    Do this instead

    Read the 2 as the position. The value is whatever the slot holds, such as true or false.

  • Common mistake

    Assuming a new boolean array needs its slots set to false before the simulation can check them.

Practice Tracing a Lookup

EASY

A boolean array named used has five slots, positions 0 to 4, all false. The program draws day 3, reads used[3], finds false, and then sets used[3] to true. Next, it draws day 3 again. Trace which slots are read and which are changed during the second draw, and state whether the program had to visit used[0], used[1], or used[2] at any point.

Hints
  • Reread how a drawn day is used as an index.
  • Ask whether the second draw needs any slot other than the one named by its index.
  • Check the step where a value of true means a shared birthday was found.

Key Takeaways

  • Sequential access visits array elements one after another in array order, while random access reaches any element directly by its position.
  • Every element of an array is equally accessible at any given time, so a program can go straight to the slot it needs without passing through earlier positions.
  • The birthday problem uses a boolean array of 365 slots, where the index is the day number and the value records whether that birthday has been seen; new boolean arrays start with false in every slot.
  • Because each drawn birthday can point to any slot, random access lets the simulation read and record a day in a single direct step.