The fastest solution is to use Fisher–Yates shuffle; java implementation exists in this SO link.
However, although it is considered a really fast and unbiased algorithm, in your case, the main problem when using Fisher–Yates shuffle (and apparently Collections.shuffle
that uses it), is that it doesn't guarantee all letters will change their initial index. You may end up having a couple of letters unchanged, which sometimes is not good (imagine some of your vowels do not change; then it may be easy to decrypt).
If the aforementioned issue is a requirement, then there are many solutions to guarantee that each character has indeed changed. I only enlist some of them.
a) Best approach, use Sattolo's algorithm.
public static void SattoloShuffle(char[] a) {
int n = a.length;
Random random = new Random();
int r;
char temp;
for (int i = n; i > 1; i--) {
// choose index uniformly in [0, i-1)
r = random.nextInt(i-1);
temp = a[r];
a[r] = a[i-1];
a[i-1] = temp;
}
}
b) A greedy approach would be to iteratively run shuffle until you have all your chars changed from their initial position.
c) You could also use an array with your characters from a-z and then create your desired shuffled array by randomly picking characters ensuring that the same character is not changed with itself e.g.:
char[] initialDictionary = new char[] { 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
int n = initialDictionary.length;
char[] encryptedDictionary = new char[n];
int i = 0;
int randomIndex;
Random random = new Random();
while (i < n) {
randomIndex = random.nextInt(n);
if (randomIndex != i && initialDictionary[randomIndex] != '0') {
encryptedDictionary[i++] = initialDictionary[randomIndex];
initialDictionary[randomIndex] = '0'; // use 0 to flag chars already used
}
}
d) In the same sense with the previous solution, you can use Lists but after selecting a valid character, remove this char from the initial list.