Генератор случайных чисел генерирует только одно случайное число
У меня есть следующая функция:
//Function to get random number
public static int RandomNumber(int min, int max)
{
Random random = new Random();
return random.Next(min, max);
}
Как я его называю:
byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);
Если я нахожу этот цикл с отладчиком во время выполнения, я получаю разные значения (это то, что я хочу).
Однако, если я поставил точку останова на две строки ниже этого кода, все члены массива "mac" имеют равное значение.
Почему это происходит?
Ответы
Ответ 1
Каждый раз, когда вы выполняете new Random()
, он инициализируется с использованием часов. Это означает, что в замкнутом цикле вы получаете одно и то же значение много раз. Вы должны сохранить один экземпляр Random
и продолжать использовать Next
в экземпляре тот же.
//Function to get a random number
private static readonly Random random = new Random();
private static readonly object syncLock = new object();
public static int RandomNumber(int min, int max)
{
lock(syncLock) { // synchronize
return random.Next(min, max);
}
}
Изменить (см. комментарии): зачем нам lock
здесь?
В принципе, Next
собирается изменить внутреннее состояние экземпляра Random
. Если мы сделаем это в то же время из нескольких потоков, вы можете утверждать, что "мы только что сделали результат еще более случайным", но то, что мы на самом деле делаем, потенциально может нарушить внутреннюю реализацию, и мы также можем начать получать одинаковые числа из разных потоков, что может быть проблемой - и может и не быть. Однако гарантией того, что происходит внутри, является большая проблема; поскольку Random
делает не гарантию безопасности потоков. Таким образом, существует два действительных подхода:
- синхронизировать, чтобы мы не обращались к нему в то же время из разных потоков
- использовать разные экземпляры
Random
для потока
Либо может быть хорошо; но mutexing одиночный экземпляр от нескольких вызывающих абонентов в то же время просто просит проблемы.
lock
достигает первого (и более простого) этих подходов; однако другой подход может быть:
private static readonly ThreadLocal<Random> appRandom
= new ThreadLocal<Random>(() => new Random());
это то, что происходит в потоке, поэтому вам не нужно синхронизировать.
Ответ 2
Для удобства повторного использования во всем приложении может помочь статический класс.
public static class StaticRandom
{
private static int seed;
private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
(() => new Random(Interlocked.Increment(ref seed)));
static StaticRandom()
{
seed = Environment.TickCount;
}
public static Random Instance { get { return threadLocal.Value; } }
}
Затем вы можете использовать статический случайный экземпляр с кодом, например
StaticRandom.Instance.Next(1, 100);
Ответ 3
Решение Mark может быть довольно дорогостоящим, поскольку его нужно синхронизировать каждый раз.
Мы можем обойти необходимость синхронизации, используя шаблон хранения, специфичный для потока:
public class RandomNumber : IRandomNumber
{
private static readonly Random Global = new Random();
[ThreadStatic] private static Random _local;
public int Next(int max)
{
var localBuffer = _local;
if (localBuffer == null)
{
int seed;
lock(Global) seed = Global.Next();
localBuffer = new Random(seed);
_local = localBuffer;
}
return localBuffer.Next(max);
}
}
Измерьте две реализации, и вы увидите значительную разницу.
Ответ 4
Мой ответ здесь:
Просто повторю правильное решение:
namespace mySpace
{
public static class Util
{
private static rnd = new Random();
public static int GetRandom()
{
return rnd.Next();
}
}
}
Итак, вы можете позвонить:
var i = Util.GetRandom();
.
Если для генерации случайных чисел вам необходим только истинный статический метод stateless, вы можете положиться на Guid
.
public static class Util
{
public static int GetRandom()
{
return Guid.NewGuid().GetHashCode();
}
}
Это будет немного медленнее, но может быть намного более случайным, чем Random.Next
, по крайней мере, из моего опыта.
Но не:
new Random(Guid.NewGuid().GetHashCode()).Next();
Создание ненужного объекта сделает его медленнее, особенно в цикле.
И никогда:
new Random().Next();
Не только он медленнее (внутри цикла), его случайность... ну не очень хорошая по мне.
Ответ 5
Я бы предпочел использовать следующий класс для генерации случайных чисел:
byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
Ответ 6
1) Как сказал Марк Гравелл, попробуйте использовать ОДИН случайный генератор. Всегда полезно добавить это в конструктор: System.Environment.TickCount.
2) Один совет. Предположим, вы хотите создать 100 объектов и предположите, что каждый из них должен иметь свой собственный генератор случайных чисел (удобно, если вы вычисляете НАГРУЗКИ случайных чисел за очень короткий промежуток времени). Если вы сделаете это в цикле (создание 100 объектов), вы можете сделать это так (для обеспечения полной случайности):
int inMyRandSeed;
for(int i=0;i<100;i++)
{
inMyRandSeed = System.Environment.TickCount + i;
.
.
.
myNewObject = new MyNewObject(inMyRandSeed);
.
.
.
}
// Usage: Random m_rndGen = new Random(inMyRandSeed);
Приветствия.
Ответ 7
Есть много решений, здесь один: если вы хотите только номер стирать буквы, а метод получает случайную и длину результата.
public String GenerateRandom(Random oRandom, int iLongitudPin)
{
String sCharacters = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
int iLength = sCharacters.Length;
char cCharacter;
int iLongitudNuevaCadena = iLongitudPin;
String sRandomResult = "";
for (int i = 0; i < iLongitudNuevaCadena; i++)
{
cCharacter = sCharacters[oRandom.Next(iLength)];
sRandomResult += cCharacter.ToString();
}
return (sRandomResult);
}
Ответ 8
Если вы хотите использовать функцию, которая создает экземпляр Random каждый раз, когда вы можете использовать это:
Threading.Thread.Sleep(1)
Dim random As New Random(System.DateTime.Now.Millisecond)
Dim secret = random.Next(10000, 100000)
Ответ 9
Это приведет к генерации случайных чисел. Подстройте его по мере необходимости.
private static void Main(string[] args)
{
var randoms = GetRandomNumbers(1, 1000, 50);
}
private static IEnumerable<int> GetRandomNumbers(int low, int high, int numberOfRandoms)
{
Random rnd = new Random();
var unique = new HashSet<int>();
do
{
int num = rnd.Next(low, high);
if (unique.Contains(num)) continue;
unique.Add(num);
} while (unique.Count < numberOfRandoms);
return unique.ToList();
}
(== Try Me ==)