Генератор случайных строк создает одну и ту же строку при нескольких вызовах - PullRequest
5 голосов
/ 03 мая 2010

Я построил генератор случайных строк, но у меня возникла проблема, из-за которой, если я вызываю функцию несколько раз, скажем, в методе Page_Load, функция возвращает одну и ту же строку дважды.

вот код

Public Class CustomStrings
    ''' <summary>'
    ''' Generates a Random String'
    ''' </summary>'
    ''' <param name="n">number of characters the method should generate</param>'
    ''' <param name="UseSpecial">should the method include special characters? IE: # ,$, !, etc.</param>'
    ''' <param name="SpecialOnly">should the method include only the special characters and excludes alpha numeric</param>'
    ''' <returns>a random string n characters long</returns>'
    Public Function GenerateRandom(ByVal n As Integer, Optional ByVal UseSpecial As Boolean = True, Optional ByVal SpecialOnly As Boolean = False) As String

        Dim chars As String() ' a character array to use when generating a random string'
        Dim ichars As Integer = 74 'number of characters to use out of the chars string'
        Dim schars As Integer = 0 ' number of characters to skip out of the characters string'

        chars = { _
         "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", "0", "1", "2", "3", _
         "4", "5", "6", "7", "8", "9", _
         "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", "!", "@", "#", "$", _
         "%", "^", "&", "*", "(", ")", _
         "-", "+"}


        If Not UseSpecial Then ichars = 62 ' only use the alpha numeric characters out of "char"'
        If SpecialOnly Then schars = 62 : ichars = 74 ' skip the alpha numeric characters out of "char"'

        Dim rnd As New Random()
        Dim random As String = String.Empty
        Dim i As Integer = 0
        While i < n
            random += chars(rnd.[Next](schars, ichars))
            System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
        End While
        rnd = Nothing
        Return random
    End Function
End Class

но если я назову что-то вроде этого

* +1007 *

ответ будет примерно таким

г * 3JQ
г * 3JQ

и во второй раз я позвоню, это будет

3QM0 $
3QM0 $

Чего мне не хватает? Я хотел бы, чтобы каждая случайная строка генерировалась как уникальная.

Ответы [ 5 ]

7 голосов
/ 03 мая 2010

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

Другими словами, это:

Random r = new Random();
int i = r.Next(1000);
r = new Random();
int j = r.Next(1000);

имеет очень высокую вероятность получения одинаковых значений в i и j.

Что вам нужно сделать, это:

  • Создайте и кэшируйте экземпляр Random, чтобы он был одним и тем же экземпляром, используемым для каждого вызова (но, к сожалению, класс не является поточно-ориентированным, поэтому по крайней мере сохраняйте кэшированную копию для каждого потока)
  • Заполните его чем-то, что меняется для каждого вызова (что немного сложнее, потому что заполнение его последовательным значением даст предсказуемые случайные числа)

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

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SO2755146
{
    public class Program
    {
        public static void Main()
        {
            List<Task> tasks = new List<Task>();
            for (int index = 0; index < 1000; index++)
                tasks.Add(Task.Factory.StartNew(() => Console.Out.WriteLine(RNG.Instance.Next(1000))));
            Task.WaitAll(tasks.ToArray());
        }
    }

    public static class RNG
    {
        private static Random _GlobalSeed = new Random();
        private static object _GlobalSeedLock = new object();

        [ThreadStatic]
        private static Random _Instance;

        public static Random Instance
        {
            get
            {
                if (_Instance == null)
                {
                    lock (_GlobalSeedLock)
                    {
                        _Instance = new Random(_GlobalSeed.Next());
                    }
                }
                return _Instance;
            }
        }
    }
}

Если вы просто хотите отобрать каждый случайный экземпляр из часов, но, по крайней мере, производить случайные последовательности для каждого потока, вы можете упростить это следующим образом:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SO2755146
{
    public class Program
    {
        public static void Main()
        {
            List<Task> tasks = new List<Task>();
            for (int index = 0; index < 1000; index++)
                tasks.Add(Task.Factory.StartNew(() => Console.Out.WriteLine(RNG.Instance.Next(1000))));
            Task.WaitAll(tasks.ToArray());
        }
    }

    public static class RNG
    {
        [ThreadStatic]
        private static Random _Instance;

        public static Random Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new Random();

                return _Instance;
            }
        }
    }
}

Это может привести к тому, что два потока начнутся очень близко друг к другу, чтобы быть посеянными с одинаковым значением, поэтому есть компромисс.

1 голос
/ 16 апреля 2012

Я использовал следующий подход для создания уникального семени.

Session["seedRandom"] = 1;

Создана переменная сеанса в page_load. Эта переменная сеанса будет увеличиваться и добавляться к DateTime.Now.Ticks

* +1007 *
private string getRandAlphaNum()
{
   int seed_value = (int)DateTime.Now.Ticks;
   seed_value = seed_value + Int32.Parse(Session["seedRandom"].ToString());
   //change the Session variable by incrementing its value to 1 after creating seed value.
   Session["seedRandom"] = Int32.Parse(Session["seedRandom"].ToString()) + 1;

   Random rand = new Random(seed_value);
   .....
   .....
}

Так что каждый раз, когда я получаю начальное значение как DateTimeNow.Ticks + Обновленная переменная сеанса

1 голос
/ 25 января 2011

Если вы хотите, чтобы реальная случайность не имела последовательности, то определенно НЕ используйте функции System.Random. Лучше использовать функции System.Security.Cryptography и в идеале убедиться, что ваше оборудование поддерживает RNG (генерирование случайных чисел), которое автоматически использует .NET.

Вот хороший пример: http://www.obviex.com/Samples/Password.aspx

1 голос
/ 03 мая 2010

Подход с уникальным номером семян

Чтобы предотвратить использование одного и того же начального значения, чтобы прекратить генерировать одну и ту же случайную последовательность, вы можете создать случайное начальное число, опустив GUID (неявно рандомизированный) до значения int с помощью функции, подобной следующей:

Private Function GetNewSeed() As Integer
    Dim arrBytes As Byte() = Guid.NewGuid().ToByteArray()  '16 bytes
    Dim seedNum As Integer = 0
    ' Boil GUID down 4 bytes at a time (size of int) and merge into Integer value
    For i As Integer = 0 To arrBytes.Length - 1 Step 4
        seedNum = seedNum Xor BitConverter.ToInt32(arrBytes, i)
    Next
    Return seedNum
End Function

Используйте возвращенное int для заполнения вашего генератора случайных чисел.

Теперь проблема решена с помощью пользовательской функции GetNewSeed.

Dim rnd1 As New Random( GetNewSeed )

Это решает корень проблемы, которая является начальным значением.

0 голосов
/ 03 мая 2010

Я сделал пару изменений, и мне кажется, это нормально. Мне не нравится использовать ключевые слова в качестве имен переменных. Обратите внимание, что я переместил случайный оператор:

Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Dim myCS As New CustomStrings
    Dim l As New List(Of String)

    For x As Integer = 1 To 10
        Dim s As String = myCS.GenerateRandom(5)
        l.Add(s)
    Next

    For x As Integer = 0 To l.Count - 1
        Debug.WriteLine(l(x))
    Next
    'Debug output
    'YGXiV
    'rfLmP
    'OVUW9
    '$uaMt
    '^RsPz
    'k&91k
    '(n2uN
    'ldbQQ
    'zYlP!
    '30kNt
End Sub

Public Class CustomStrings

    Private myRnd As New Random()
    Public Function GenerateRandom(ByVal n As Integer, _
                                   Optional ByVal UseSpecial As Boolean = True, _
                                   Optional ByVal SpecialOnly As Boolean = False) As String

        Dim ichars As Integer = 74 'number of characters to use out of the chars string'
        Dim schars As Integer = 0 ' number of characters to skip out of the characters string'

        Dim chars() As Char = New Char() {"A"c, "B"c, "C"c, "D"c, "E"c, "F"c, _
                                          "G"c, "H"c, "I"c, "J"c, "K"c, "L"c, _
                                          "M"c, "N"c, "O"c, "P"c, "Q"c, "R"c, _
                                          "S"c, "T"c, "U"c, "V"c, "W"c, "X"c, _
                                          "Y"c, "Z"c, "0"c, "1"c, "2"c, "3"c, _
                                          "4"c, "5"c, "6"c, "7"c, "8"c, "9"c, _
                                          "a"c, "b"c, "c"c, "d"c, "e"c, "f"c, _
                                          "g"c, "h"c, "i"c, "j"c, "k"c, "l"c, _
                                          "m"c, "n"c, "o"c, "p"c, "q"c, "r"c, _
                                          "s"c, "t"c, "u"c, "v"c, "w"c, "x"c, _
                                          "y"c, "z"c, "!"c, "@"c, "#"c, "$"c, _
                                          "%"c, "^"c, "&"c, "*"c, "("c, ")"c, _
                                          "-"c, "+"c}


        If Not UseSpecial Then ichars = 62 ' only use the alpha numeric characters out of "char"'
        If SpecialOnly Then schars = 62 : ichars = 74 ' skip the alpha numeric characters out of "char"'

        Dim rndStr As String = String.Empty
        Dim i As Integer = 0
        While i < n
            rndStr += chars(Me.myRnd.Next(schars, ichars))
            System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
        End While
        Return rndStr
    End Function
End Class

End Class
...