Reputation: 18753
Code:
public char[] numbers = new char[] { '3', '4', '5' };
public char[] letters = new char[] { 'X', 'T', 'M', 'W', 'Y', 'L' };
//Generate a String with only 2 of the above letters and the rest filled
//with the numbers the characters shouldn't be always next to one another
I looked at some of the other solutions: var random = new Random();
for (int i=0; i<50; i++)
{
var result = new string(
Enumerable.Repeat(chars, 8)
.Select(s => s[random.Next(s.Length)])
.ToArray());
}
but they just generate random strings not from a smaller character set nor do they limit the number of letters versus number of numbers.
Examples: Valid
X333Y453
X3L45453
XL333333
4L333X333
333L45L3
Invalid
5533Y453
333L45453
XL33L333
Can only have 2 characters and since there are 8 total characters there should be 6 numbers
Upvotes: 0
Views: 1457
Reputation: 50201
With just a couple of helper methods:
public static class CombinatoricsHelper {
private static readonly Random random = new Random();
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable) {
List<T> list = enumerable.ToList();
int position = list.Count;
while (position > 0) {
int selectedIndex = random.Next(position);
yield return list[selectedIndex];
position -= 1;
list[selectedIndex] = list[position];
}
}
public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> enumerable, int count) {
List<T> list = enumerable.ToList();
int remaining = count;
while (remaining > 0) {
yield return list[random.Next(list.Count)];
remaining -= 1;
}
}
}
And your arrays:
public char[] numbers = new char[] { '3', '4', '5' };
public char[] letters = new char[] { 'X', 'T', 'M', 'W', 'Y', 'L' };
This becomes very straightforward:
return new string(letters
.TakeRandom(2)
.Concat(numbers.TakeRandom(6))
.Shuffle()
.ToArray()
);
Sample output:
3X44334L 335W555Y LX443355 53T3Y333 W443W534 M5444W44 3553T33Y X4443W45 433M533Y 54L445M4 55X5W444 543443XX
Even though my answer uses more code than other answers, I recommend it because the actual algorithmic part of interest (how you're creating the string) is separated from the implementation details that can be glossed over a bit (Shuffle
and TakeRandom
). That is, once someone understands what Shuffle
and TakeRandom
do, not only can they be reused but they make the rest of the code much clearer.
As a case in point to prove my assertion, my original answer here for you didn't allow the 2 selected letters to be the same letter. Because I separated the high-level algorithm from the implementation of the algorithm's logical parts, I fixed that by simply changing letters.Shuffle().Take(2) ...
to letters.TakeRandom(2) ...
. Now you can see the power of using functions (even though they seem big and unwieldy sometimes) instead of just writing a raw implementation that does exactly one thing and nothing else.
Note: my Shuffle
method is the Fisher-Yates shuffle in disguise.
A Friendly WARNING To Those Seeking Much Randomness
Please consider this quote from the Wikipedia Fisher-Yates Shuffle page, section:
The built-in pseudorandom number generator provided by many programming languages and/or libraries may often have only 32 bits of internal state, which means it can only produce 232 different sequences of numbers. If such a generator is used to shuffle a deck of 52 playing cards, it can only ever produce a very small fraction of the 52! ≈ 2225.6 possible permutations. It's impossible for a generator with less than 226 bits of internal state to produce all the possible permutations of a 52-card deck.
So be warned. You need to swap in a much more heavy-duty random number generator than the built-in .Net Random
if you want to shuffle more than 12 items when running a 32-bit RNG, or more than 20 items with a 64-bit RNG, and somewhat reliably get a relatively equal chance for all possible outcomes (see the chart "Size of PRNG seeds and the largest list where every permutation could be reached" on the right side of the above article). Consider, for example, using the System.Security.Cryptography.RNGCryptoServiceProvider, but still pay attention to how much entropy it can provide! Don't use this to shuffle cards unless you really know what you're doing.
Upvotes: 2
Reputation: 547
Here is my solution. It starts by generating a string of 6 numbers, then inserts a random letter into a random index twice. That way, you should have an even distribution for all possible outputs.
An important thing to note about the C# Random
class is that you should not reinstantiate it for every generated string or else you will be using the same seed and get the same output every time.
public string GenerateString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 6; i++)
{
sb.Append(numbers[random.Next(numbers.Length)]);
}
int letterIndex = random.Next(6);
sb.Insert(letterIndex, letters[random.Next(letters.Length)]);
letterIndex = random.Next(7);
sb.Insert(letterIndex, letters[random.Next(letters.Length)]);
return sb.ToString();
}
Upvotes: 0
Reputation: 474
Here is one line solution if you like it
var result = new string(Enumerable.Repeat(0, 6).Select(o => new {value = numbers[random.Next(numbers.Length)], sort = random.Next()}).
Union(Enumerable.Repeat(0, 2).Select(o => new {value = letters[random.Next(letters.Length)], sort = random.Next()})).
OrderBy(o => o.sort).Select(o => o.value).ToArray());
Upvotes: 0
Reputation: 100527
Feels like there are 2 groups that need to be merged with particular rules:
Approximate code:
IEnumerable<char> PickRandom(char[] source, int count)
{
return Enumerable.Repeat(1, count)
.Select(s => source[random.Next(source.Length)]);
}
var randomLetters = PickRandom(letters, 2);
var randomNumbers = PickRandom(numbers, 6);
int lettersGroup1 = random.Next(2);
int group1 = random.Next(6);
int group2 = random.Next(6 - group1);
var result = randomNumbers.Take(group1)
.Concat(randomLetters.Take(lettersGroup1))
.Concat(randomNumbers.Skip(group1).Take(group2))
.Concat(randomLetters.Skip(lettersGroup1))
.Concat(randomNumbers.Skip(group1 + group2));
var stringResult = String.Join("", result);
Note: the code is strict exercise in LINQ, there are definitely more readable/efficient ways to build string of characters.
Upvotes: 2
Reputation: 1625
var random = new Random();
for (int i=0; i<50; i++){
var resultLetters = new string(
Enumerable.Repeat(chars, 2)
.Select(s => letters[random.Next(letters.Length)]));
}
Will select two letters from the available pool. You can do the same with the numbers (but 5 times). If you want the order to be random you can do it by just shuffling the array in the end (Collections.shuffle will shuffle an Collection).
Upvotes: 0