\b(?:(?<fizzbuzz>(?:(?:[369]|[258][0369]*[147]|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*(?:[28]|[147][0369]*[147]))*(?:0|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*5))+)|(?<buzz>\d*[05])|(?<fizz>(?:[0369]|[258][0369]*[147]|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147]))+))\b
insanity, as i'd like to call it. although it isn't fizzbuzz per se, it's the best you can get with regex. here's a regexr link to it with sample text.
this matches a number (within word boundaries) if it falls under any of the three groups:
fizzbuzz
, which picks up numbers divisible by both 3 and 5, i.e. 15;buzz
, which picks up numbers divisible by 5; andfizz
, which picks up numbers divisible by 3.
the pattern for buzz
is simply \d*[05]
, where it checks if a number ends with a 0 or a 5, like how one would normally check divisibility by 5. fizz
's pattern however is more complex as it checks the total sum of the digits, modulo 3. fizzbuzz
is similar to fizz
, except it also checks divisibility by 5 -- more specifically, it checks for (<fizz>)*(0|<A>5)
, where A is a pattern that sums up to a remainder of 1.
the patterns for fizz
and fizzbuzz
were generated from hand-drawn DFA machines and compiled to regex via state removal. their formal definitions being:
Mfizz = (Q, Σ, δ, q0, F), where:
- Q = {S0, S1, S2} (each representing a remainder of the accumulating sum)
- Σ = {0, 1, 2, ..., 9}
- q0 = S0
- F = S0 and
- δ as the following table:
0, 3, 6, 9 | 1, 4, 7 | 2, 5, 8 | |
---|---|---|---|
S0 | S0 | S1 | S2 |
S1 | S1 | S2 | S0 |
S2 | S2 | S0 | S1 |
Mfizzbuzz = (Q, Σ, δ, q0, F), where:
- Q = {S0, S1, S2, S3} (same thing but with an extra state S3 for 0 remainders that don't end in 0 or 5)
- Σ = {0, 1, 2, ..., 9}
- q0 = F = S0
- F = S0 and
- δ as the following table:
0 | 1, 4, 7 | 2, 8 | 3, 6, 9 | 5 | |
---|---|---|---|---|---|
S0 | S0 | S1 | S2 | S3 | S2 |
S1 | S1 | S2 | S3 | S1 | S0 |
S2 | S2 | S3 | S1 | S2 | S1 |
S3 | S0 | S1 | S2 | S3 | S2 |
or, if you prefer eye candy, here's some graph versions from my notes:
\b(?:((?:(?:[369]|[258][0369]*[147]|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*(?:[28]|[147][0369]*[147]))*(?:0|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*5))+)|(\d*[05])|((?:[0369]|[258][0369]*[147]|(?:[147]|[258][0369]*[258])(?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147]))+))\b
styled under python highlighting because it looks nice.
import re
pattern = re.compile(r"""
\b
(?:
(?P<fizzbuzz>
(?:
(?:
[369]
|
[258][0369]*[147]
|
(?:[147]|[258][0369]*[258])
(?:[0369]|[147][0369]*[258])*
(?:[28]|[147][0369]*[147])
)*
(?:
0
|
(?:[147]|[258][0369]*[258])
(?:[0369]|[147][0369]*[258])*
5
)
)+
)
|
(?<buzz>
\d*[05]
)
|
(?<fizz>
(?:
[0369]
|
[258][0369]*[147]
|
(?:[147]|[258][0369]*[258])
(?:[0369]|[147][0369]*[258])*
(?:[258]|[147][0369]*[147])
)+
)
)
\b
""", re.VERBOSE)
i just thought it would be funny
- Check number divisibility with regular expressions (2012), Stack Overflow
- How to convert finite automata to regular expressions? (2012), CS Stack Exchange
- Deterministic Finite Automata 1, 2, 3, 4 (2016), Neso Academy look this stuff is very new to me alright