Ok, if you want it faster still, and your close to integer numbers are machine-size integers - here are two equivalent implementations - in Mathematica compiled to C, and Java. It is an interesting problem to compare performance, we will observe that Java code is speed-equivalent to C code here, modulo small extra time needed for data transfer.
The idea is to obtain a list of integers from those numbers close to ones, and a list of their positions (this is close in spirit to what @Rojo did in his now deleted answer). But then, I will create a copy of the original list and modify it in-place with Part
.
So, our top-level function is then
ClearAll[roundClose];
roundClose[data_, f_] :=
Module[{copy = data},
(copy[[#[[2]]]] = #[[1]]) &[ f[copy]];
copy];
where f
is a function which returns a list {ints, positions}
.
Using Compile
Here is a function using Compile
:
fn =
Compile[{{data, _Real, 1}},
Module[{i = 1, ctr = 0, ints = Table[0, {Length[data]}],
pos = Table[0, {Length[data]}]},
Do[
If[data[[i]] == Floor[data[[i]]],
ints[[++ctr]] = Round[data[[i]]];
pos[[ctr]] = i
],
{i, Length[data]}
];
{Take[ints, ctr], Take[pos, ctr]}
],
CompilationTarget -> "C",
RuntimeOptions -> "Speed"]
Using Java
Assuming that we have the Java reloader loaded, we compile this class:
JCompileLoad@
"import java.util.Arrays;
public class RoundCloseToInteger{
public static int [][] roundClose(double [] nums ){
int[] resultNums = new int[nums.length];
int[] resultPos = new int[nums.length];
int ctr = 0;
for(int i=0;i<nums.length;i++){
double num = nums[i];
if(num ==((double)Math.floor(num))) {
resultNums[ctr]=(int)Math.round(num);
resultPos[ctr++]=i+1;
}
}
resultNums = Arrays.copyOf(resultNums,ctr);
resultPos = Arrays.copyOf(resultPos,ctr);
return new int[][]{resultNums,resultPos};
}
}"
The function to be used is then RoundCloseToInteger`roundClose
.
Benchmarks
Here is a test data:
ld = Range[0, 1*^6, 0.5];
Testing now:
(r1 = Chop[# - Floor@#] + Floor@# &@ld);//AbsoluteTiming
(r2 = roundClose[ld,RoundCloseToInteger`roundClose]);//AbsoluteTiming
(r3 = roundClose[ld,fn]);//AbsoluteTiming
(*
{1.6113282,Null}
{0.5341797,Null}
{0.4873047,Null}
*)
r1==r2==r3
(* True *)
Remarks
We get roughly the same 3x speed-up with both compiled to C and Java versions, w.r.t. the code of @Mr.Wizard. The reason for it is that, for such light-weight operations as Floor
or Round
, the time scales linearly with the numbers of runs through the list, which is 3 for the code of @Mr.Wizard and only 1 for the present code.
We can see that for such long lists, Java code is pretty much speed-equivalent to C code generated by Compile
. One can experiment with data transfer and confirm that the timing difference is of the same order as needed to transfer back and forth the data. However, the smaller the size of the list, the more overhead will be encoutered for java calls, in proportion to the total running time.
I included the Java solution because it is a good and simple case study to see the relative speed on (Compile
-generated) C vs Java, in a simple setting. To my mind, this shows that Java is a viable alternative. One advantage of Java is that it is cross-platform, meaning that once you compiled a given class on some machine, you can bring it to a computer not equipped with C compiler, and it will run there, and also, you won't face a compilation to C overhead.