Генератор случайных чисел - переменная длина - PullRequest
2 голосов
/ 04 января 2012

В настоящее время моя программа (см. Ниже) генерирует случайные строки (из чисел) длиной от 8 до 12 символов.

public static string GenerateNewCode(int CodeLength)
{
    string newCode = String.Empty;
    int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
    Random random = new Random(seed);

    // keep going until we find a unique code       `
    do
    {
        newCode = random.Next(Convert.ToInt32(Math.Pow(10, CodeLength - 1)), Convert.ToInt32(Math.Pow(10, CodeLength) - 1)).ToString("0000");
    }
    while (!ConsumerCode.isUnique(newCode));

    // return
    return newCode;
}

Однако, это проблема метода, когда codeLength равен 10 или больше, это вызывает ошибку, потому что 10 9 больше int32.MaxValue.

Не уверен, как обойти эту проблему.

Ответы [ 5 ]

17 голосов
/ 04 января 2012

Вместо генерации числа от 0 до 10 n -1, а затем преобразования его в строку, генерирования n чисел от 0 до 9, преобразования каждого в строку и объединения их вместе.

Отмечу, что этот метод дает числа в диапазоне (скажем, для n = 4) от 0 до 9999; Ваша оригинальная версия дает числа от 1000 до 9999. Вы можете сгенерировать первую цифру от 1 до 9 вместо 0 до 9, если хотите сохранить это свойство.

Конечно, способ, которым я это делаю, является более случайным, чем n - объединенные значения 0 - 9.

Пожалуйста, объясните, почему вы в это верите. Мне очень интересно узнать, почему люди верят в ложь.

Не могли бы вы создать Guid.NewGuid (). Tostring () и обрезать наиболее значимые символы, чтобы получить случайную строку длины N?

Вы могли бы , но вы не должны . Идентификаторы GUID не гарантируются случайными, и никакое правильное подмножество битов идентификатора GUID не может быть уникальным. Брать биты из GUID и ожидать, что биты будут иметь свойства GUID, все равно что снимать руль с самолета и ожидать, что руль полетит. Никогда и никогда не делай этого.

Скорее используйте инструменты для целей, для которых они были предназначены. GUID были разработаны для предоставления глобально уникальных идентификаторов, поэтому используйте их ни для чего другого.

Вопрос, который вы не задавали, но, вероятно, должны:

Что еще не так или подозрительно с моим кодом?

int seed = unchecked(DateTime.Now.Ticks.GetHashCode()); 

Что на самом деле делает этот код? Во-первых, случайный класс уже использует текущее время в качестве начального числа; это не нужно Во-вторых, какова цель выражения "1032 *" без проверки арифметики, которое не содержит арифметики ? В-третьих, зачем вы получили хеш-код? Вы не балансируете хеш-таблицу!

Random random = new Random(seed);  

Если этот метод вызывается дважды в одном и том же тике, то начальное число будет одинаковым, и, следовательно, последовательность случайных чисел будет одинаковой, что означает, что вы будете потенциально долго ждать, потому что каждое сгенерированное число будет столкновение в вашем «уникальном» наборе.

Лучшей техникой является создание статического экземпляра объекта Random, посеянного один раз. Если ваша программа однопоточная, то это не проблема. Если он многопоточный, убедитесь, что у вас нет доступа к Random из нескольких потоков; это не потокобезопасно, и режим сбоя нарушения его безопасности потока не хорош.

   newCode = random.Next(Convert.ToInt32(Math.Pow(10, CodeLength - 1)),                         Convert.ToInt32(Math.Pow(10, CodeLength) - 

Действительно ли необходимо отрабатывать мощность дважды за цикл, когда она всегда одинакова? Проработайте это один раз, прежде чем начнется цикл. Код будет короче, понятнее и быстрее.

while (!ConsumerCode.isUnique(newCode));    

Если коллекция заполнена, это зацикливается навсегда. Если коллекция почти заполнена, то это цикл в течение long времени. Это потенциально плохая техника для генерации уникальных случайных чисел в диапазоне. Делайте это только в том случае, если вы заранее знаете, что число сгенерированных чисел намного больше, чем максимальный размер коллекции.

Так как бы выглядел этот метод для этой функции?

Я бы хотел сделать что-то подобное. Во-первых, я хотел бы бесконечный набор цифр:

// Yield an infinite sequence of pseudo-random digits 0-9
// This method is not thread-safe.
private static Random random = new Random();
static IEnumerable<int> Digits()
{
    while(true)
        yield return random.Next(0, 10);
}

Теперь я могу генерировать уникальную строку цифр:

// Generates a random code of given length. If it is not
// in the set, adds it and returns the code. If it is
// already in the set, tries again. 
static string AddUniqueCode(int length, HashSet<string> set)
{
    while(true)
    {
        string code = string.Join(null, Digits().Take(length));
        if (set.Add(code))
            return code;
    }
}    

Мне нравятся мои короткие методы.

Вы также можете сделать так, чтобы метод «Цифры» использовал криптостойкость вместо псевдослучайности, если непредсказуемость важна для вас. псевдослучайные числа легко предсказать.

4 голосов
/ 04 января 2012

Ваш код делает то же самое без каких-то странных строк:

public static string GenerateNewCode(int CodeLength)
{
    Random random = new Random();
    StringBuilder output = new StringBuilder();

    do
    {   
        for (int i = 0; i < CodeLength; i++)
        {
            output.Append(random.Next(0, 10));
        }
    }
    while (!ConsumerCode.isUnique(output.ToString()));
    return output.ToString();
}
3 голосов
/ 04 января 2012

Генерация случайного числа от 0 до 9;добавить '0' к нему;брось его на чарса;теперь у вас есть случайная цифра.Повторите столько раз, сколько вам нужно.

Как это: char c = (char)(random.Next(10) + '0');

0 голосов
/ 04 января 2012

Вы можете создать два случайных числа, где первое случайное число имеет длину 6-10, а второе случайное число имеет длину 2. После того, как два числа сгенерированы, объедините их, чтобы получить строку длиной 8-12.

Пример:

public static string GenerateNewCode(int CodeLength)
{
string newCode = String.Empty;
int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
Random random = new Random(seed);

// keep going until we find a unique code       `
do
{   
// The firstPart int will be a random number that has a length of 6, 7, 8, ,9, or 10 digits
int firstPart = random.Next(100000,2147483647);

// The secondPart int will be a random number that has a length of two digits
int secondPart = random.Next(10,99);        

// Concatenate firstPart and secondPart. This will create a string that has a length of 8, 9, 10, 11, or 12 chars.
newCode = firstPart.ToString() + secondPart.ToString();

}
while (!ConsumerCode.isUnique(newCode));

// return
return newCode;
}
0 голосов
/ 04 января 2012

Один метод, который вы могли бы использовать Random.NextDouble (), чтобы вернуть число в диапазоне от 0..1, затем масштабировать его до 0..26 и использовать таблицу поиска для возврата буквенного символа на основе индекса.Например:

char lookup = new char[] { 'a', 'b', 'c' ... 'z' }; // ensure length 26
int index = (int)(random.NextDouble() * 25.0);
return lookup[index]

Более эффективный метод предполагает использование таблицы ascii для преобразования целого числа в желаемый буквенно-цифровой символ.

С уважением,

...