Fastest square number test

Update

Sorry for my ignorance not taking into account that the question specifically asked for a Mathematica 7 solution. I updated the complete post.

Mathematica 7

In Mathematica 7 we don't have the option the compile code into a C-library which includes the thread parallelization which can be turned on when using RuntimeAttributes->Listable and Parallelization->True. Therefore, acl's solution will not run in Mathematica 7 because the RuntimeAttributes option for Compile was introduced in version 8.

This leaves the possibility to not compile the used function and make it a normal Mathematica function where you can set the attribute Listable. I tried this, but it was horrible slow.

After a bit of research I found a nice solution which uses some number-properties in base 16. Since (at least in V7) it seems somewhat hard to return lists of True|False, I use 0 and 1 where 0 means no square.

fPat = Compile[{{numbers, _Integer, 1}},
   With[{l = Length[numbers]},
    Module[{n = 0, i = 0, h = 0, test = 0.0, result = Table[0, {l}]},
     For[i = 1, i <= l, ++i,
      n = numbers[[i]];
      h = BitAnd[15, n];
      If[h > 9, Continue[]];
      If[h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8,
       test = Sqrt[n];
       result[[i]] = test == Floor[test];
       ];
      ];
     result
     ]
    ]
   ];

Comparing this with the almost one-liner of Sal gives

data = Table[i, {i, 1, 10^6}];

fSal = Compile[{{n, _Integer}}, 
   With[{test = Sqrt[n]}, Floor[test] == test]];

BarChart[{Timing[fSal /@ data][[1]], Timing[fPat[data]][[1]]
  }, ChartLabels -> {"Sal Mangano", "Patrick V7"}, 
 ChartStyle -> {Gray, Green}]

I leave it to you to decide whether such a C-like programming style is worth the small speed up.

enter image description here

Mathematica 8

The fastest way (using Mathematica only) I know is to compile a C-library and process all data in parallel. Since most computers these days have at least 2 cores, this gives a boost. In Mathematica 8 the compilation to a C-library does not copy the data when it is called.

To make the computation parallel you have to use the Parallization option and the compiled function must be Listable. If you are sure of your input-data, you can additionally switch off most of the data-checks by using RuntimeOptions set to "Speed".

Update I include here the parallelized version of the Mathematica 7 code above:

data = Table[i, {i, 1, 10^6}];

fSal = Compile[{{n, _Integer}}, 
   With[{test = Sqrt[n]}, Floor[test] == test]];
fAcl = Compile[{{n, _Integer}}, 
   With[{test = Sqrt[n]}, Floor[test] == test], 
   RuntimeAttributes -> {Listable}];
fPat = Compile[{{n, _Integer}}, 
   With[{test = Sqrt[n]}, Floor[test] == test], 
   CompilationTarget -> "C", RuntimeAttributes -> {Listable}, 
   Parallelization -> True, RuntimeOptions -> "Speed"];

fPat2 = Compile[{{numbers, _Integer, 1}},
   With[{l = Length[numbers]},
    Module[{n = 0, i = 0, h = 0, test = 0.0, result = Table[0, {l}]},
     For[i = 1, i <= l, ++i,
      n = numbers[[i]];
      h = BitAnd[15, n];
      If[h > 9, Continue[]];
      If[h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8,
       test = Sqrt[n];
       result[[i]] = test == Floor[test];
       ];
      ];
     result
     ]
    ], CompilationTarget -> "C", RuntimeAttributes -> {Listable}, 
   Parallelization -> True, RuntimeOptions -> "Speed"
   ];

BarChart[{Timing[fSal /@ data][[1]], Timing[fAcl[data]][[1]], 
  Timing[fPat[data]][[1]],
  Timing[fPat2[data]][[1]]}, 
 ChartLabels -> {"Sal Mangano", "acl", "Patrick", 
   "Patrick V7 parallel"}, 
 ChartStyle -> {Gray, Gray, Darker[Green], Green}]

The results here come from my MacBook in battery-save mode which has 2 Intel cores. The disadvantage is that you need a C-compiler installed on your system which is most likely not true for the majority of Mathematica users.

enter image description here


I voted for all three previous answer because they all taught me something. However they, being Compile solutions, are not helpful with big integers.

At least on my system, Sal Mangano's code appears reducible to this without loss of speed:

isSq2 = Compile[n, Floor@# == # & @ Sqrt @ n];

For big integers between about 2*10^9 and 2*10^11 I am currently using this code from Sasha:

SquareQ =
    JacobiSymbol[#, 13] =!= -1 &&
    JacobiSymbol[#, 19] =!= -1 &&
    JacobiSymbol[#, 17] =!= -1 &&
    JacobiSymbol[#, 23] =!= -1 &&
    IntegerQ@Sqrt@# &;

For integers larger than that I am using code (modified) from Daniel Lichtblau:

SquareQ2 = # == Round@# & @ Sqrt @ N[#, Log[10`, #] + $MachinePrecision] &;

I don't think there are any built-in functions for this but the following is probably fast enough for most purposes.

isSq = Compile[{{n, _Integer}}, With[{test = Sqrt[n]},
    Floor[test] == test]];

Does 1 million integers in under a second.

Timing[Table[isSq[i], {i, 1, 1000000}]][[1]]
(*
0.76195
*)

This is under 2 orders of magnitude faster than the un-compiled equivalent, by the way.