Retina, 66 63 45 43 36 bytes
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Despite the title saying Retina, this is just a plain .NET regex which accepts unary representations of Loeschian numbers.
Inputs 999 and 1000 take well under a second.
Try it online! (The first line enables a linefeed-separated test suite, and the next two take care of the conversion to unary for convenience.)
Explanation
The solution is based on the classification that the input can be written as i*i + j*(i + j)
for positive i
and non-negative j
(since we don't have to handle input 0
), and that n*n
is just the sum of the first n
odd integers. Golfing this was an interesting exercise in forward references.
A "forward reference" is when you put a backreference inside the group it refers to. Of course that doesn't work when the group is used the first time, since there is nothing to be backreferenced yet, but if you put this in a loop, then the backreference gets the previous iteration's capture each time. This in turn, let's you build up a larger capture with each iteration. This can be used to craft very compact patterns for things like triangular numbers, squares and Fibonacci numbers.
As an example, using the fact that squares are just sums of the first n
odd integers, we can match a square input like this:
(^.|..\1)+$
On the first iteration, ..\1
can't work, because \1
doesn't have a value yet. So we start with ^.
, capturing a single character into group 1
. On subsequent iterations, ^.
no longer matches due to the anchor, but now ..\1
is valid. It matches two more characters than the previous iteration and updates the capture. This way we match increasing odd numbers, getting a square after each iteration.
Now unfortunately, we can't use this technique as is. After matching i*i
, we need to get i
as well, so that we can multiply it by j
. A simple (but long) way to do this is to make use of the fact that matching i*i
takes i
iterations, so that we've captured i
things in group 1
. We could now use balancing groups to extract this i
, but like I said that's expensive.
Instead, I figured out a different way to write this "sum of consecutive odd integers" that also yields i
in a capturing group at the end. Of course the i
th odd number is just 2i-1
. This gives us a way to increment the forward reference only by 1 on each iteration. That's this part:
^()(\1(?<1>.\1))+
This ()
just pushes an empty capture onto group 1
(initialising i
to 0
). This is pretty much equivalent to the ^.|
in the simple solution above, but using |
in this case would be a bit trickier.
Then we have the main loop (\1(?<1>.\1))
. \1
matches the previous i
, (?<1>.\1)
then updates group 1
with i+1
. In terms of the new i
, we've just matched 2i-1
characters. Exactly what we need.
When we're done, we've matched some square i*i
and group 1
still holds i
characters.
The second part is closer to the simple square matching I showed above. Let's ignore the backreference to 1
for now:
(.(?(4).\1))*
This is basically the same as (^.|..\4)*
, except that we can't make use of ^
because we're not at the start of the string. Instead we make use of a conditional, to match the additional .\1
only when we've already used group 4
. But in effect this is exactly the same. This gives us j*j
.
The only thing that's missing is the j*i
term. We combine this with the j*j
by making use of the fact that the j*j
computation still takes j
iterations. So for each iteration we also advance the cursor by i
with \1
. We just need to make sure not to write that into group 4
, because that would mess with matching consecutive odd numbers. That's how we arrive at the:
(\1(.(?(4).\1)))*