From charlesreid1

(Created page with "==Problem Statement== 500px ==Flag== {{ProjectEulerFlag}}")
 
(Restructure page to match Project Euler/42 format: add detailed Approach section mirroring the Java Digit DP implementation (via update-page on MediaWiki MCP Server))
 
(4 intermediate revisions by one other user not shown)
Line 3: Line 3:
[[Image:ProjectEuler112.png|500px]]
[[Image:ProjectEuler112.png|500px]]


Working from left-to-right, if no digit is exceeded by the digit to its left it is called an increasing number — for example, 134468.


Similarly, if no digit is exceeded by the digit to its right it is called a decreasing number — for example, 66420.


We shall call a positive integer that is neither increasing nor decreasing a "bouncy" number — for example, 155349.


Clearly there cannot be any bouncy numbers below one-hundred, but just over half of the numbers below one-thousand (525) are bouncy. In fact, the least number for which the proportion of bouncy numbers first reaches 50% is 538.


Surprisingly, bouncy numbers become more and more common and by the time we reach 21780 the proportion of bouncy numbers is equal to 90%.


==Flag==
Find the least number for which the proportion of bouncy numbers is exactly 99%.
 
==Approach==
 
The goal is to find the least <math>N</math> such that the proportion of bouncy numbers <math>\le N</math> is exactly 99%. Equivalently, the count of non-bouncy numbers <math>NB(N)</math> must satisfy:
 
<math>
NB(N) = \frac{N}{100}
</math>
 
This implies <math>N</math> must be a multiple of 100.
 
A number is non-bouncy if it is either increasing or decreasing. Numbers with all identical digits (e.g., 555) are both increasing and decreasing, so by inclusion-exclusion:
 
<math>
NB(N) = I(N) + D(N) - F(N)
</math>
 
where <math>I(N)</math>, <math>D(N)</math>, and <math>F(N)</math> are the counts of increasing, decreasing, and flat (all digits identical) numbers <math>\le N</math>, respectively.
 
===Digit DP for Increasing Numbers===
 
The function <math>I(N)</math> counts positive integers <math>x \le N</math> whose digits are non-decreasing from left to right. This is computed using a recursive Digit DP with memoization.
 
'''State variables:'''
* <code>index</code> — current digit position being processed (0 to number of digits in <math>N</math>)
* <code>prevDigit</code> — the previous digit placed (0–9)
* <code>isLessThan</code> — whether the prefix constructed so far is already strictly less than the corresponding prefix of <math>N</math>
* <code>isLeadingZero</code> — whether we are still in the leading-zero phase (no non-zero digit has been placed yet)
 
'''Transitions:''' At each position, iterate over possible digits <math>d</math> from 0 up to the bound (9 if <code>isLessThan</code> is true, otherwise the digit of <math>N</math> at that position). If <code>isLeadingZero</code> is active and <math>d = 0</math>, the leading-zero state continues. Once a non-zero digit is placed, subsequent digits must satisfy <math>d \ge \text{prevDigit}</math> to maintain the increasing property.
 
The DP result includes 0, so 1 is subtracted to count only positive integers.
 
===Digit DP for Decreasing Numbers===
 
The function <math>D(N)</math> is analogous but enforces non-increasing digits (<math>d \le \text{prevDigit}</math>). The memoization table uses an extra slot (index 10) to represent the initial/leading-zero state where no previous digit constraint applies.
 
===Counting Flat Numbers===
 
Flat numbers are those with all digits identical (e.g., 7, 22, 999). The count <math>F(N)</math> is computed directly:
 
* For each digit length less than the length of <math>N</math>, there are 9 flat numbers (one for each digit 1–9).
* For numbers with the same length as <math>N</math>, each candidate flat number (111…1 through 999…9) is compared lexicographically to <math>N</math>; if it is <math>\le N</math>, it is counted.
 
===Non-Bouncy Count===
 
<math>
NB(N) = I(N) + D(N) - F(N)
</math>
 
===Search Strategy===
 
The search begins at <math>N = 1{,}000{,}000</math> (where the proportion of bouncy numbers is approximately 98.7%) and increments by 100. At each step, <math>NB(N)</math> is computed via the Digit DP routines. The first <math>N</math> satisfying <math>NB(N) = N / 100</math> is the answer.
 
A timeout and an upper bound (50,000,000) are included as safety guards.
 
==Solution==
 
Link to solution: https://git.charlesreid1.com/cs/euler/src/branch/main/java/Problem112.java
 
==Flags==


{{ProjectEulerFlag}}
{{ProjectEulerFlag}}

Latest revision as of 14:40, 16 June 2026

Problem Statement

ProjectEuler112.png

Working from left-to-right, if no digit is exceeded by the digit to its left it is called an increasing number — for example, 134468.

Similarly, if no digit is exceeded by the digit to its right it is called a decreasing number — for example, 66420.

We shall call a positive integer that is neither increasing nor decreasing a "bouncy" number — for example, 155349.

Clearly there cannot be any bouncy numbers below one-hundred, but just over half of the numbers below one-thousand (525) are bouncy. In fact, the least number for which the proportion of bouncy numbers first reaches 50% is 538.

Surprisingly, bouncy numbers become more and more common and by the time we reach 21780 the proportion of bouncy numbers is equal to 90%.

Find the least number for which the proportion of bouncy numbers is exactly 99%.

Approach

The goal is to find the least $ N $ such that the proportion of bouncy numbers $ \le N $ is exactly 99%. Equivalently, the count of non-bouncy numbers $ NB(N) $ must satisfy:

$ NB(N) = \frac{N}{100} $

This implies $ N $ must be a multiple of 100.

A number is non-bouncy if it is either increasing or decreasing. Numbers with all identical digits (e.g., 555) are both increasing and decreasing, so by inclusion-exclusion:

$ NB(N) = I(N) + D(N) - F(N) $

where $ I(N) $, $ D(N) $, and $ F(N) $ are the counts of increasing, decreasing, and flat (all digits identical) numbers $ \le N $, respectively.

Digit DP for Increasing Numbers

The function $ I(N) $ counts positive integers $ x \le N $ whose digits are non-decreasing from left to right. This is computed using a recursive Digit DP with memoization.

State variables:

  • index — current digit position being processed (0 to number of digits in $ N $)
  • prevDigit — the previous digit placed (0–9)
  • isLessThan — whether the prefix constructed so far is already strictly less than the corresponding prefix of $ N $
  • isLeadingZero — whether we are still in the leading-zero phase (no non-zero digit has been placed yet)

Transitions: At each position, iterate over possible digits $ d $ from 0 up to the bound (9 if isLessThan is true, otherwise the digit of $ N $ at that position). If isLeadingZero is active and $ d = 0 $, the leading-zero state continues. Once a non-zero digit is placed, subsequent digits must satisfy $ d \ge \text{prevDigit} $ to maintain the increasing property.

The DP result includes 0, so 1 is subtracted to count only positive integers.

Digit DP for Decreasing Numbers

The function $ D(N) $ is analogous but enforces non-increasing digits ($ d \le \text{prevDigit} $). The memoization table uses an extra slot (index 10) to represent the initial/leading-zero state where no previous digit constraint applies.

Counting Flat Numbers

Flat numbers are those with all digits identical (e.g., 7, 22, 999). The count $ F(N) $ is computed directly:

  • For each digit length less than the length of $ N $, there are 9 flat numbers (one for each digit 1–9).
  • For numbers with the same length as $ N $, each candidate flat number (111…1 through 999…9) is compared lexicographically to $ N $; if it is $ \le N $, it is counted.

Non-Bouncy Count

$ NB(N) = I(N) + D(N) - F(N) $

Search Strategy

The search begins at $ N = 1{,}000{,}000 $ (where the proportion of bouncy numbers is approximately 98.7%) and increments by 100. At each step, $ NB(N) $ is computed via the Digit DP routines. The first $ N $ satisfying $ NB(N) = N / 100 $ is the answer.

A timeout and an upper bound (50,000,000) are included as safety guards.

Solution

Link to solution: https://git.charlesreid1.com/cs/euler/src/branch/main/java/Problem112.java

Flags