Improving the algorithm
The Algorithm is still inefficient. The while loop cycles many more times then there are results.
letters | results | iterations
--------------------------------
4 | 24 | 64
5 | 120 | 325
6 | 720 | 1,956
7 | 5,040 | 13,699
8 | 40,320 | 109,600
9 | 362,880 | 986,409
10 | 3,628,800 | 9,864,099
So there is a lot of room to optimise if there is a pattern in the character swapping.
Outputting all the values used for each iteration you immediately see some patterns. Every time i
is set to 0 the next iteration will always fall to the else
clause. So do the else clause inline after setting i=0
saving the number of results in cycles. But this is still not significant.
The testing if i < counter[i]
and n= (i%2?counter[i]:0)
seam very wasteful. So I then look at the pattern of swaps. I then find a lot of repeating patterns.
I find that I can reduce the iterations down to only the number of results. Then I find that every second swap is always the fist and second characters, so I inline every second swap. Then a second pattern, from the start the swaps are (0 & 2, 0 & 1, 0 & 2, 0 & 1), 0 & 3, then the bracketed set repeats.
So there is additional cycles I can remove, plus using the common swap method
temp = chars[2];
chars[2] = chars[0];
chars[0] = temp;
// then
temp = chars[1];
chars[1] = chars[0];
chars[0] = temp;
The temp
holds one of the values of the next swap, so we don't need to store the temp value for the next swap
temp = chars[2];
chars[2] = chars[0];
chars[0] = temp;
// then
chars[0] = chars[1];
chars[1] = temp;
After finding a few more patterns I hit upon a new method. The code is much longer as I repeat inline rather than loop (every cycle counts), and is tailored to the letter count. I also know that in javascript using literal values is slightly better than using variables, because the function is now tailored to letter count I can use literals where possible.
The 5 letter word solution
// predefined solution memory
var anagrams5 = new Uint8Array(120 * 5);
function anagramer5(input) {
var chars,length,i,index,tc,n,cc,anagrams;
index = 0;
length = 5;
chars = new Uint8Array(5);
chars[0] = input.charCodeAt(0);
chars[1] = input.charCodeAt(1);
chars[2] = input.charCodeAt(2);
chars[3] = input.charCodeAt(3);
chars[4] = input.charCodeAt(4);
anagrams = anagrams5;
anagrams.set(chars)
index += 5;
cc = 0;
while(cc < 5){
i = 0;
while(i < 3){ // swap (2 & 0, 2 & 0, 2 & 0,1 & 0),3 & i
// swap
tc = chars[2];
chars[2] = chars[0];
chars[0] = tc;
anagrams.set(chars,index);
index += 5;
// swap
chars[0] = chars[1];
chars[1] = tc;
anagrams.set(chars,index);
index += 5;
tc = chars[2];
chars[2] = chars[0];
chars[0] = tc;
anagrams.set(chars,index);
index += 5;
// swap
chars[0] = chars[1];
chars[1] = tc;
anagrams.set(chars,index);
index += 5;
// swap
tc = chars[3];
chars[3] = chars[i];
chars[i++] = tc;
anagrams.set(chars,index);
index += 5;
// swap
tc = chars[1];
chars[1] = chars[0];
chars[0] = tc;
anagrams.set(chars,index);
index += 5;
}
cc ++;
if(cc < 4){ // swap 4 & 0 , 1 & 0
// swap
tc = chars[4];
chars[4] = chars[0];
chars[0] = tc;
anagrams.set(chars,index);
index += 5;
// swap
chars[0] = chars[1];
chars[1] = tc;
anagrams.set(chars,index);
index += 5;
}
}
// swap
tc = chars[1];
chars[1] = chars[0];
chars[0] = tc;
anagrams.set(chars,index);
index += 5;
// swap
chars[0] = chars[2];
chars[2] = tc;
anagrams.set(chars,index);
return anagrams
}
Running this on the word "coder" I compare it to the current accepted answer. The benchmarks show another significant improvement.
Raw benchmark results
============================================
Performance test. : AnagramsOf_codereview
Use strict....... : true
Data view........ : false
Duplicates....... : 4
Cycles........... : 197
Samples per cycle : 1000
---------------------------------------------
Test : 'BM method'
35µs 24647 samples
34µs 24811 samples
35µs 24563 samples
34µs 24787 samples
Test : 'Current Best'
155µs 24707 samples
157µs 24599 samples
159µs 24496 samples
158µs 24390 samples
---------------------------------------------
Test : 'BM method'
Mean : 35µs ±1µs (*) 98808 samples
---------------------------------------------
Test : 'Current Best'
Mean : 157µs ±2µs (*) 98192 samples
-All ----------------------------------------
Mean : 0.096ms Total time : 18847.750ms 197000 samples
(*) Error rate approximation does not represent the variance.
So that is up now an improvement of 4.49 which holds true for words up to 10 letters long (I assume that is true for any length word).
The problem is of course the world length is no longer any length. But there is another solution, as yet untested. I will add that to this answer when I get time to implement.