Skip to content
Environment 5 min read

A 100-year flood can happen twice. Here is what the odds actually mean

Observation recorded

A flood described as a “100-year” event does not buy a place another 99 quiet years. The label describes an annual probability for exceeding a particular threshold. Under a simple model with a constant 1% annual chance and independent years, the chance of at least one exceedance over 30 years is 26.03%. The chance of exceeding that threshold in at least two different years is 3.61%.

Those are calculations, not measurements of an actual river. We reproduced a published government reference table with Python, then tested how the result changes when the annual probability changes. The distinction matters: a recurrence label, a forecast, and a history of observed floods answer different questions.

Start with the threshold, not the calendar

The National Weather Service’s flooding FAQ explains the 100-year label as a 1% chance in any given year. The event is reaching or exceeding a specified flood magnitude. It is not a prediction that an event will arrive at regular century-long intervals.

For example, fix one river location and one flood-level threshold. In the simple calculation below, each year either crosses that threshold or does not. A year with several crossings still counts as one exceedance year. Moving to another river or changing the threshold means changing the question.

Recent NASA coverage comparing the 2026 El Niño with earlier events is a reminder that climate patterns have different regional effects. It does not supply a local flood probability, and we do not infer one from it here.

Reconstructing an official reference table

Maine’s flood-information page publishes rounded probabilities for several annual chances and time horizons. We used its 16 main table entries as a reference dataset, with annual probabilities of 10%, 4%, 2%, and 1% and horizons of 1, 10, 20, and 30 years.

If the annual probability is p, the chance of no exceedance in one year is 1 - p. With independence and a constant probability, the chance of no exceedance in n years is (1 - p)**n. Taking the complement gives:

   P(at least one exceedance in n years) = 1 - (1 - p)**n

Here are our calculated values, rounded to two decimal places:

Annual chance1 year10 years20 years30 years
10%10.00%65.13%87.84%95.76%
4%4.00%33.52%55.80%70.61%
2%2.00%18.29%33.24%45.45%
1%1.00%9.56%18.21%26.03%

Every calculated entry is within half a percentage point of the published whole-number entry. This checks that our implementation reproduces the reference table’s rounding. It does not show that independent, constant-risk years perfectly describe any real catchment.

Calculated probability of at least one exceedance over 30 years for annual probabilities of 1, 2, 4, and 10 percent. All curves use independent years and constant probabilities.

Original Python-generated figure. Curves are analytic probabilities, not observed flood records. The input table and assumptions are available below.

Why 30% is not the answer

Multiplying 30 years by 1% produces 0.30. That is the expected number of exceedance years in this model. It is not the probability of at least one exceedance.

An expectation averages across possible outcomes, including sequences with zero, one, or several exceedance years. A probability asks whether a specified condition occurs. Adding annual chances treats overlapping possibilities as though they were separate outcomes; the complement calculation avoids that mistake.

For the same reason, 100 independent years at 1% annual probability produce about a 63.40% chance of at least one exceedance, rather than certainty.

Could it happen twice?

To find the probability of two or more exceedance years, subtract the probabilities of zero and exactly one from one:

   p, n = 0.01, 30
two_or_more = 1 - (1-p)**n - n*p*(1-p)**(n-1)
print(100 * two_or_more)  # 3.614799831...

This is an unconditional probability over a 30-year window. It is a different question from “What is next year’s chance after an exceedance this year?” In the independent constant-probability model, the answer to that second question stays 1%. A previous event has not exhausted an allowance.

What changes when the probability changes?

Real conditions need not remain constant. To show why that assumption matters, we calculated three illustrative scenarios for the same 30-year horizon:

AssumptionChance of at least one exceedance
1% every year26.03%
A linear increase from 1% in year one to 2% in year 3036.46%
2% every year45.45%

For changing annual probabilities p_i, the independent-year expression becomes 1 - product(1 - p_i). The increasing-risk row is a sensitivity test we chose, not a projection for a location or a claim about how rapidly climate change alters flood risk.

The NOAA National Severe Storms Laboratory discusses flood causes and the meaning of the annual-chance label. Turning that label into a local estimate requires hydrology, local measurements, and current conditions. Our calculation supplies none of those inputs. It also does not estimate damage, flood depth, or the number of individual storms.

Reproduce the analysis

Download the input table and assumptions, standard-library Python script, and full numerical results. Put the script and input JSON in the same directory, then run python analyze.py. No account or additional package is required.

The most useful question to ask about a recurrence label is therefore: what magnitude, at which location, over what time span, and under which assumptions? The number “100” answers only part of that question.

For another way to explore a complementary-probability model, try our radioactive-decay simulator. Its physical assumptions concern nuclei rather than rivers, but computing what has not happened is again a useful route to computing what has.

Related observations
Reviews Which Python course should a science learner choose? Start with the task Environment We changed NASA’s temperature baseline. The warming trend did not move Programming write a program to calculate electricity bill in python
Transmit