Compilation is a certainly a good idea if you're going the brute-force route. So let's first tackle that, and afterwards come up with better strategy.
Compilation
A performance pitfall when compiling functions is that sometimes the compiled function just calls the main evaluation loop, effectively gaining nothing. We can check this with CompilePrint
:
Needs["CompiledFunctionTools`"]
a // CompilePrint
(...)
Result = I3
1 I3 = MainEvaluate[(...)]
2 Return
The call to MainEvaluate
is the culprit here.
So let's rewrite your function such that it does compile properly. Using Pickett's and wuyingddg suggestions, we end up with:
PerformSearch = Compile[
{
{startValue, _Integer},
{increment, _Integer}
},
NestWhile[
# + increment &,
startValue,
IntegerDigits[#^2][[-1 ;; 1 ;; -2]] =!= {9, 8, 7, 6, 5, 4, 3, 2, 1} &
]
]
Let's check if this did compile ok:
PerformSearch // CompilePrint
(...)
1 I4 = I0
2 I3 = I4
3 I7 = Square[ I3]
4 T(I1)2 = IntegerDigits[ I7, I5]]
5 T(I1)0 = Part[ T(I1)2, T(I1)1Span]
6 B0 = CompareTensor[ I6, R2, T(I1)0, T(I1)3]]
7 if[ !B0] goto 12
8 I3 = I4
9 I7 = I3 + I1
10 I4 = I7
11 goto 2
12 Return
Ok, that looks much better. We can now perform the search, but not after we've determined the range of possible solutions:
max = Floor @ Sqrt @ FromDigits @ Riffle[Range[9], 9] (* 138902662 *)
min = Ceiling @ Sqrt @ FromDigits @ Riffle[Range[9], 0] (* 101010102 *)
As an aside, note that max^2
is below $MaxMachineInteger
on 64-bit systems. But on 32-bit it isn't, which causes PerformSearch
to switch back to the uncompiled code.
Keeping Mark McClure's comment in mind, we'll cheat a bit and start from the maximum to find immediately:
result = PerformSearch[max, -1]
138901917
How much faster is this than starting from the minimum?
(result - min)/(max - result) // N
50861.5
Roughly $\mathcal{O}(10^4)$ times faster, not bad! (Starting from the mimimum takes about 30 seconds on my machine btw).
Last but not least, let's double-check if we've obtained the correct number:
result^2
19293742546274889
If you multiply result
with 10, you've got your desired integer.
Non-brute-force
The approach above scanned roughly 38 million (!) integers in the worst-case scenario (starting from the minimum). Other answers to the OP's question have shown that you can and should go one better than that. Here's my take on an efficient general solution, using an iterative step-by-step process:
FindIntegerRoots[
pattern : {(0|1|2|3|4|5|6|7|8|9|Verbatim[_]) ..},
power_Integer: 2
] /; power > 1 := LetL[
{
maxRoot = Floor[FromDigits[pattern /. Verbatim[_] -> 9]^(1/power)],
subpatterns = pattern[[Length@pattern - # + 1;; -1]]& /@ Range@IntegerLength@maxRoot,
RootCases = Function[
{digitSequences, patt},
Select[
digitSequences,
And[
# <= maxRoot,
MatchQ[IntegerDigits[#^power, 10, Length @ patt], patt]
] & @ FromDigits @ # &
]
],
InsertDigits = Replace[
#,
{digits___Integer} :> Sequence @@ ({#, digits} & /@ Range[0, 9]),
{1}
] &
},
FromDigits /@ Fold[
RootCases[InsertDigits @ #1, #2] &,
{{}},
subpatterns
] ~ RootCases ~ pattern
]
Note that this depends on Leonid's LetL
function.
It is reasonably fast for the OP's question:
FindIntegerRoots @ Riffle[Range[9], _] // AbsoluteTiming
{1.930638, {138901917}}
But how efficient is it? Here's a nice graph of the amount of checks it performs in this case:

That's about 296 thousand checks in total; certainly much less than 38 million!
But the nice thing about FindIntegerRoots
is that it works on any pattern, not just the OP's case:
FindIntegerRoots @ {_, 9}
{3, 7}
FindIntegerRoots @ { 1, _, 4}
{12}
FindIntegerRoots @ { 1, _, 6}
{14}
And if you're adventurous, you may even ask for roots of different powers:
FindIntegerRoots[{_, _, _, 5}, 3]
{5, 15}
IntegerDigits[#^2][[ ;; ;; 2]] == {1,2,3,4,5,6,7,8,9,0}
instead. – Pickett yesterday10 Floor[Ceiling[Sqrt[1929394959697989990]]/10]
and step down by 10 from there, you'll get the solution almost immediately. – Mark McClure yesterday