Regex (ECMAScript), 392 358 328 224 206 165 164 162 161 bytes
-1 byte by using ECMAScript non-participating capture group behavior
-2 bytes by by calculating \$F_{2n+3}\$ differently
-1 byte by moving around a remainder
The techniques that need to come into play to match Fibonacci numbers with an ECMAScript regex (in unary) are a far cry from how it's best done in most other regex flavors. The lack of forward/nested backreferences or recursion means that it is impossible to directly count or keep a running total of anything. The lack of lookbehind makes it often a challenge even to have enough space to work in.
Many problems must be approached from an entirely different perspective, and seem unsolvable until the arrival of some key insight. It forces you to cast a much wider net in finding which mathematical properties of the numbers you're working with might be able to be used to make a particular problem solvable.
In March 2014, this is what happened for Fibonacci numbers. Looking at the Wikipedia page, I initially couldn't figure out a way, though one particular property seemed tantalizingly close. Then the mathematician teukon outlined a method that made it quite clear it would be possible to do, using that property along with another one. He was reluctant to actually construct the regex. His reaction when I went ahead and did it:
You're crazy! ... I thought you might do this.
As with my other ECMAScript unary math regex posts, I'll give a warning: I highly recommend learning how to solve unary mathematical problems in ECMAScript regex. It's been a fascinating journey for me, and I don't want to spoil it for anybody who might potentially want to try it themselves, especially those with an interest in number theory. See that post for a list of consecutively spoiler-tagged recommended problems to solve one by one.
So do not read any further if you don't want some unary regex magic spoiled for you. If you do want to take a shot at figuring out this magic yourself, I highly recommend starting by solving some problems in ECMAScript regex as outlined in that post linked above.
The challenge I initially faced: A positive integer x is a Fibonacci number if and only if 5x2 + 4 and/or 5x2 - 4 is a perfect square. But there is no room to calculate this in a regex. The only space we have to work in is the number itself. We don't even have enough room to multiply by 5 or take the square, let alone both.
teukon's idea on how to solve it (originally posted here):
The regex is presented with a string of the form ^x*$
, let z be its length. Check whether or not z is one of the first few Fibonacci numbers by hand (up to 21 should do). If it is not:
- Read a couple of numbers, a Use forward look-aheads to build a2, ab, and b2.
- Assert that either 5a2 + 4 or 5a2 - 4 is a perfect square (so a must be Fn-1 for some n).
- Assert that either 5b2 + 4 or 5b2 - 4 is a perfect square (so b must be Fn).
- Check that z = F2n+3 or z = F2n+4 by using the earlier built a2, ab, and b2, and the identities:
- F2n-1 = Fn2 + Fn-12
- F2n = (2Fn-1 + Fn)Fn
In brief: these identities allow us to reduce the problem of checking that a given number is Fibonacci to checking that a pair of much smaller numbers are Fibonacci. A little algebra will show that for large enough n (n = 3 should do), F2n+3 > Fn + 5Fn2 + 4 so there should always be enough space.
And here is a mockup of the algorithm in C which I wrote as a test before implementing it in regex. My final golfed regex is rather different. There turned out to be no need to find both \$a\$ and \$b\$, because \$b\$ is the Lucas number \$L_n\$, which along with \$F_n\$ can be used to derive \$F_{2n+2}\$ and \$F_{2n+3}\$ as needed:
- \${L_n}^2 = 5{F_n}^2±4\$
- \$L_{2n} = ({5{F_n}^2 + {L_n}^2}) / 2 = 5{F_n}^2±2 = {L_n}^2∓2\$
- \$F_{2n} = L_n F_n\$
- \$F_{2n+1} = ({L_{2n} + F_{2n}})/2\$
- \$F_{2n+2} = F_{2n} + F_{2n+1}\$
- \$F_{2n+3} = F_{2n+1} + F_{2n+2}\$
So with no further ado, here is the regex:
^(?=(x*).*(?=x{4}(x{5}(\1{5}))(?=\2*$)\3+$)(|x{4})(?=xx(x*)\5)\4(x(x*))(?=(\6*)\7+$)(?=\6*$\8)\6*(?=x\1\7+$)(x*)(\9x?)|)(\9\10(\5\10)\12?|xx?x?|x{5}|x{8}|x{21})$
Try it online!
And the pretty-printed, commented version:
^ # tail = N = input number
(?=
(x*) # \1+1 = potential number for which 5*(\1+1)^2 ± 4 is a
# perfect square; this is true iff \1+1 is a Fibonacci number,
# which we shall call F_n. Outside the surrounding lookahead
# block, \1+1 is guaranteed to be the largest number for which
# this is true such that \1 + 5*(\1+1)^2 + 4 <= N.
.*
(?= # tail = (\1+1) * (\1+1) * 5 + 4
x{4}
( # \2 = (\1+1) * 5
x{5}
(\1{5}) # \3 = \1 * 5
)
(?=\2*$)
\3+$
)
(|x{4}) # \4 = parity - determined by whether the index of Fibonacci
# number \1+1 is odd or even;
(?=
xx # tail = arithmetic mean of (\1+1) * (\1+1) * 5 and \6 * \6
# = ((F_n * F_n * 5) + (L_n * L_n)) / 2 = L_{2n}
(x*)\5 # \5 = floor(tail / 2) = floor(L_{2n} / 2)
)
\4
# require that the current tail is a perfect square
(x(x*)) # \6 = potential square root, which will be the square root
# after the following two lookaheads; \7 = \6-1
(?=(\6*)\7+$) # \8 = must be zero for \6 to be a valid square root
(?=\6*$\8)
# \6 is now the Lucas number L_n corresponding to the Fibonacci number F_n.
\6*
(?=x\1\7+$) # tail = F_{2n} = L_n * F_n = \6 * (\1+1), where \6 is larger
(x*)(\9x?) # \9 = floor(tail / 2);
# \10 = ceil(tail / 2); the remainder tail % 2 will always be
# the same as the remainder discarded by \5, because
# F_{2n} is odd iff L_{2n} is odd, thus this ceil()
# can complement the floor() of \5 when adding \5 + \10
| # Allow everything above to be skipped, resulting in all
# capture groups being unset.
)
(
# Note that if the above was skipped using the empty alternative in the lookahead,
# the following will evaluate to 0. This relies on ECMAScript NPCG behavior.
\9\10(\5\10) # \12 = F_{2n+1} = (L_{2n} + F_{2n})/2 = \5 + \10;
# head = F_{2n+2} = F_{2n} + F_{2n+1}
# = \9+\10 + \12
\12? # head = F_{2n+3} = F_{2n+2} + F_{2n+1} (optionally)
# = head + \12
|
xx?x?|x{5}|x{8}|x{21} # The Fibonacci numbers 1, 2, 3, 5, 8, 21 cannot be handled
# by our main algorithm, so match them here; note, as it so
# happens the main algorithm does match 13, so that doesn't
# need to be handled here.
)$
The multiplication algorithm is not explained in those comments, but is briefly explained in a paragraph of my abundant numbers regex post.
I was maintaining six different versions of the Fibonacci regex: four that ratchet from shortest length to fastest speed and use the algorithm explained above, and two others that use a different, much faster but much more lengthy algorithm, which as I found can actually return the Fibonacci index as a match (explaining that algorithm here is beyond the scope of this post, but it's explained in the original discussion Gist). I don't think I would maintain that many very-similar versions of a regex again, because at the time I was doing all my testing in PCRE and Perl, but my regex engine is fast enough that concerns of speed are not as important anymore (and if a particular construct is causing a bottleneck, I can add an optimization for it) – though I'd probably again maintain one fastest version and one shortest version, if the difference in speed were big enough.
The "return the Fibonacci index minus 1 as a match" version (not heavily golfed):
Try it online!
All of the versions are on github with the full commit history of golf optimizations:
regex for matching Fibonacci numbers - short, speed 0.txt (the shortest but slowest one, as in this post)
regex for matching Fibonacci numbers - short, speed 1.txt
regex for matching Fibonacci numbers - short, speed 2.txt
regex for matching Fibonacci numbers - short, speed 3.txt
regex for matching Fibonacci numbers - fastest.txt
regex for matching Fibonacci numbers - return index.txt