It appears that you're trying to ensure unique selections of characters from a string
.
What you're looking for is a shuffle, or a partial shuffle to get a subset of characters.
A Fisher-Yates shuffle would do the job efficiently from a computational point-of-view.
Here's the full implementation:
private Random _random = new Random();
public T[] FisherYates<T>(T[] source)
{
T[] output = source.ToArray();
for (var i = 0; i < output.Length; i++)
{
var j = _random.Next(i, output.Length);
(output[i], output[j]) = (output[j], output[i]);
}
return output;
}
That will take an array of type T
and return a fully shuffled array of the same size.
Note: Calling .ToArray()
on an array calls Array.CopyTo
under-the-hood, so it's efficient.
If you just want a subset of characters then this implementation allows you to specify the number of items you want:
public T[] FisherYates<T>(T[] source, int take)
{
T[] output = source.ToArray();
take = take < output.Length ? take : output.Length;
for (var i = 0; i < take; i++)
{
var j = _random.Next(i, output.Length);
(output[i], output[j]) = (output[j], output[i]);
}
return output.Take(take).ToArray();
}
This only iterates the Fisher-Yates loop the enough times to get the number of characters you need. So, if "best" means most efficient, then this is best.
So, let's try it:
string memorableWord = "Memorable";
for (int i = 0; i < 10; i++)
Console.WriteLine(
String.Concat(
FisherYates(memorableWord.ToCharArray(), 4)));
That gave me:
Mleb
eoMe
Mber
lamM
eMlb
emlr
Mela
eebM
mrol
raeb
Now, if you just want "best" to be easiest to code, then this works:
char[] selection =
memorableWord
.OrderBy(x => _random.Next())
.Take(4)
.ToArray();
And, if you really just want unique indices, then this works too:
int[] selection =
Enumerable
.Range(0, memorableWord.Length)
.OrderBy(x => _random.Next())
.Take(4)
.ToArray();