某些数字和字母的随机字符串 - 只有一定次数

时间:2015-09-11 16:28:29

标签: c#

代码:

    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

我看了一些其他解决方案:             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());
        }

但它们只是生成随机字符串而不是来自较小的字符集,也不会限制字母数量与数字的数量。

示例: 有效

  

X333Y453

     

X3L45453

     

XL333333

     

4L333X333

     

333L45L3

无效

  

5533Y453

     

333L45453

     

XL33L333

只能有2个字符,因为总共有8个字符,所以应该有6个数字

5 个答案:

答案 0 :(得分:2)

感觉有两组需要与特定规则合并:

  • 来自一组(字母)的2个随机字符,可能还有重复
  • 来自其他集合(数字)的6个随机字符,重复
  • 有时字母之间应该有数字 - 第二组应分成最多3个部分:{0-6},{0-6},{其余}

近似代码:

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);

注意:LINQ中的代码是严格的练习,肯定有更多可读/有效的方法来构建字符串。

答案 1 :(得分:2)

只需几个辅助方法:

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;
        }
    }
}

你的阵列:

public char[] numbers = new char[] { '3', '4', '5' };
public char[] letters = new char[] { 'X', 'T', 'M', 'W', 'Y', 'L' };

这变得非常简单:

return new string(letters
    .TakeRandom(2)
    .Concat(numbers.TakeRandom(6))
    .Shuffle()
    .ToArray()
);

示例输出:

3X44334L
335W555Y
LX443355
53T3Y333
W443W534
M5444W44
3553T33Y
X4443W45
433M533Y
54L445M4
55X5W444
543443XX

尽管我的答案使用了比其他答案更多的代码,但我推荐它,因为感兴趣的实际算法部分(你如何创建字符串)与可以稍微掩饰的实现细节分开({ {1}}和Shuffle)。也就是说,一旦有人理解TakeRandomShuffle做了什么,不仅可以重用它们,而且可以使代码的其余部分更加清晰。

作为证明我的断言的一个例子,我在这里的原始答案并不允许2个选定的字母是相同的字母。因为我将高级算法与算法的逻辑部分的实现分开,所以我通过简单地将TakeRandom更改为letters.Shuffle().Take(2) ...来解决这个问题。现在你可以看到使用函数的强大功能(即使它们看起来有些大而且有些笨拙),而不仅仅是编写一个完全只做一件事的原始实现。

注意:我的letters.TakeRandom(2) ...方法是伪装的Fisher-Yates shuffle

对那些寻求大量随机性的友好警告

请考虑Wikipedia Fisher-Yates Shuffle page, section

中的这句话
  

许多编程语言和/或库提供的内置伪随机数生成器通常只有32位内部状态,这意味着它只能生成232个不同的数字序列。如果使用这样的发电机来洗牌一副52张扑克牌,它只会产生52张牌中的一小部分! ≈2 225.6 可能的排列。内部状态小于226位的发生器不可能产生52张牌的所有可能的排列。

所以请注意。如果要在运行32位RNG时调整超过12项,或者在运行32位RNG时超过20项,则需要交换比内置.Net Shuffle更重的随机数生成器。 64位RNG,并且在一定程度上可靠地获得所有可能结果的相对平等机会(参见图表&#34; PRNG种子的大小和可以达到每个排列的最大列表&#34;在上述文章的右侧)。例如,考虑使用System.Security.Cryptography.RNGCryptoServiceProvider,但仍然要注意它可以提供多少熵!除非你真的知道自己在做什么,否则不要用它来洗牌。

答案 2 :(得分:0)

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)]));
    }

将从可用池中选择两个字母。你可以用数字做同样的事情(但是5次)。如果你想让订单是随机的,你可以通过最后改组数组来做到这一点(Collections.shuffle将洗牌一个Collection)。

答案 3 :(得分:0)

如果您喜欢,这是一行解决方案

CREATE OR REPLACE PROCEDURE POPULATETARGET IS
TYPE KEYS_T IS TABLE OF SOMETABLE.KEY%TYPE;
L_KEYS KEYS_T;
V_DAY NUMBER;
SRC_CODE_FETCH VARCHAR2(200);
V_SRC_CODE VARCHAR2 (4000);
RC SYS_REFCURSOR;

BEGIN
V_DAY := 20150826;
SRC_CODE_FETCH := 'SELECT SRC_CODE FROM SOURCE';

OPEN RC FOR SRC_CODE_FETCH;
    LOOP
        FETCH RC INTO V_SRC_CODE;
        EXIT WHEN RC%NOTFOUND;
            EXECUTE IMMEDIATE V_SRC_CODE BULK COLLECT INTO L_KEYS USING V_DAY;
            FORALL x IN L_KEYS.FIRST..L_KEYS.LAST
                INSERT INTO TARGET VALUES L_KEYS(x);   
    END LOOP;
    CLOSE RC;   
END;

答案 4 :(得分:0)

这是我的解决方案。它首先生成一个包含6个数字的字符串,然后将随机字母插入随机索引两次。这样,您应该为所有可能的输出均匀分配。

关于C#Random类的一个重要注意事项是,您不应该为每个生成的字符串重新实例化它,否则您将使用相同的种子并且每次都获得相同的输出。

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();
}