난수 생성기는 하나의 난수 만 생성


765

다음과 같은 기능이 있습니다.

//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배열 의 모든 멤버 가 동일한 값을 갖습니다.

왜 그런 일이 발생합니까?


20
사용 new Random().Next((int)0xFFFF, (int)0xFFFFFF) % 256);하는 것보다 더 나은 "무작위"숫자가 나오지 않습니다..Next(0, 256)
bohdan_trotsenko

이 NuGet 패키지가 도움 될 수 있습니다. 그것은 정적의 제공 Rand.Next(int, int)종자 재사용 문제에 잠금 또는 실행하지 않고 임의의 값으로 정적에 대한 액세스를 제공하는 방법
ChaseMedallion

답변:


1042

할 때마다 new Random()시계를 사용하여 초기화됩니다. 이것은 타이트한 루프에서 같은 값을 여러 번 얻는다는 것을 의미합니다. 단일 임의 인스턴스를 유지 하고 동일한 인스턴스 에서 다음 을 계속 사용해야 합니다.

//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스레드 당 다른 인스턴스 사용

어느 쪽이든 괜찮을 수 있습니다. 그러나 동시에 여러 호출자 의 단일 인스턴스를 뮤텍스 화하는 것은 문제를 요구합니다.

lock이러한 방법의 첫 번째 (간단) 달성; 그러나 다른 접근 방식은 다음과 같습니다.

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

이것은 스레드 별이므로 동기화 할 필요가 없습니다.


19
일반적으로 여러 정적 스레드가 동시에 호출하지 않도록 보장하기 어렵 기 때문에 모든 정적 메소드는 스레드로부터 안전해야합니다. 일반적으로 인스턴스 (비 정적) 메소드를 스레드로부터 안전하게 만들 필요 는 없습니다 .
Marc Gravell

5
@Florin – "스택 기반"의 차이는 없습니다. 정적 필드는 "외부 상태"만큼이나 호출자간에 절대적 으로 공유됩니다. 인스턴스를 사용하면 스레드마다 다른 인스턴스 (공통 패턴)를 가질 가능성이 높습니다. 정적으로,됩니다 보장 그들은 모두 주 ([ThreadStatic] 포함하지 않음) 것을.
Marc Gravell

2
@gdoron에 오류가 있습니까? "잠금"은 실이 서로에 걸려 넘어지는 것을 방지해야합니다.
Marc Gravell

6
@Dan 객체가 공개적으로 노출되지 않는 경우 가능합니다. (매우 이론적 인) 위험은 다른 스레드가 예상치 못한 방식으로 스레드에 고정되어 있다는 것입니다.
Marc Gravell

3
@ smiron 잠금 이외의 무작위를 사용하는 것 같습니다. 잠금을 사용하면 잠그는 내용에 대한 모든 액세스가 차단되지는 않으며 동일한 인스턴스에서 두 개의 잠금 문이 동시에 실행되지 않도록합니다. 그래서 lock (syncObject)경우에만 도움이 될 것입니다 모든 random.Next() 통화도 내 있습니다 lock (syncObject). 당신이 설명하는 시나리오도 올바른 일어날 경우 lock사용, 그것은 또한 매우 가능성 (예를 들어, 단일 스레드 시나리오에서 발생 Random미묘하게 분류됩니다).
Luaan

118

응용 프로그램 전체에서 쉽게 재사용 할 수 있도록 정적 클래스가 도움이 될 수 있습니다.

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

62

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

두 가지 구현을 측정하면 큰 차이가 나타납니다.


12
잠금이 설정되지 않은 경우 잠금이 매우 저렴합니다. 이의가 제기 되었더라도 "현재 숫자로 무언가를 수행하십시오"코드가 가장 흥미로운 시나리오에서 잠금 비용을 감소시킬 것으로 예상합니다.
Marc Gravell

4
동의하면 잠금 문제가 해결되지만 사소한 문제에 대한 매우 복잡한 해결책은 아닙니다. 하나가 아닌 난수를 생성하기 위해``두 ''줄의 코드를 작성해야합니다. 이것은 간단한 코드 한 줄을 읽는 것을 절약하는 것이 정말로 가치가 있습니까?
EMP

4
+1 Random시드를 얻기 위해 추가 글로벌 인스턴스를 사용하는 것이 좋습니다. 또한 ThreadLocal<T>.NET 4에 도입 된 클래스를 사용하여 코드를 더욱 단순화 할 수 있습니다 (Phil 은 아래에 썼습니다 ).
Groo

40

내 대답은 여기에서 :

올바른 솔루션을 반복 하십시오 .

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

속도가 느릴뿐만 아니라 (루프 내에서) 무작위성은 ... 나에게 따르면 좋지 않습니다 ..


10
Guid 사건에 동의하지 않습니다. Random 클래스는 균일 한 분포를 구현합니다. Guid에서는 그렇지 않습니다. Guid의 목표는 균일하게 분산되지 않은 고유 한 것입니다 (그리고 그 구현은 대부분 임의성과 반대되는 일부 하드웨어 / 기계 속성을 기반으로합니다).
Askolein

4
Guid 생성의 균일 성을 입증 할 수 없다면, 무작위로 사용하는 것은 잘못입니다 (해시는 균일 성에서 다른 단계 일 것입니다). 마찬가지로, 충돌은 문제가되지 않습니다 : 충돌의 균일 성입니다. 더 이상 Guid 세대가 하드웨어를 사용하지 않는 것에 대해 RTFM에 가고 싶습니다. 나쁜
점이

5
"임의"에 대한 두 가지 이해가 있습니다 : 1. 패턴의 부족 또는 2. 확률 분포 (1에 포함 된 2)에 의해 설명 된 진화에 따른 패턴의 부족 . Guid 예제는 case 2가 아니라 case 1에 맞습니다. 반대로 : Randomclass는 case 2와 일치합니다 (따라서 case 1도). 사례 2 가 아닌 경우 에만 Random귀하 의 사용법을 대체 할 수 있습니다 . 사례 1은 아마도 질문에 대답하기에 충분할 것입니다 . 그러나 명확하게 말하지 않았다 (ps : 이 유니폼 )Guid+HashGuid+Hash
Askolein

2
@Askolein 그냥 몇 가지 테스트 데이터를 들면, 나는 모두의 여러 배치 실행 RandomGuid.NewGuid().GetHashCode()엔트 (를 통해 fourmilab.ch/random를 ) 모두 유사 무작위입니다. new Random(Guid.NewGuid().GetHashCode())동기화 된 "마스터" Random를 사용하여 "자식"에 대한 시드를 생성 하는 것과 마찬가지로 작동합니다 Random. 물론 시스템이 Guid를 생성하는 방법에 따라 다릅니다. 심지어 암호 무작위입니다. 그래서 오늘날 Windows 또는 MS SQL은 괜찮아 보입니다. 그러나 모노 및 모바일은 다를 수 있습니다.
Luaan

2
@EdB 앞서 언급했듯이 Guid (대수)는 고유하지만 GetHashCode.NET의 Guid는 문자열 표현에서 파생됩니다. 내가 좋아하는 결과물은 상당히 무작위입니다.
nawfal

27

차라리 다음 클래스를 사용하여 난수를 생성합니다.

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);

30
나는 다운 투표권 자 중 하나는 아니지만 표준 PNRG는 진정한 시드를 제공합니다. 즉 알려진 시드에서 시퀀스를 반복적으로 재현 할 수 있어야합니다. 때로는 투명한 실제 암호화 RNG의 비용 이 너무 높습니다. 때로는 암호화 RNG가 필요합니다. 말하기위한 코스 말.
Marc Gravell

4
문서 에 따르면 이 클래스는 스레드로부터 안전하므로 선호됩니다.
롭 교회

두 개의 임의 문자열이 하나가 될 확률은 얼마입니까? 문자열이 단지 3 자라면 나는 이것이 높은 확률로 일어날 것이라고 생각하지만 길이가 255 자라면 동일한 임의의 문자열을 가질 수 있습니까? 아니면 알고리즘에서 발생할 수 없다는 것을 보장합니까?
Lyubomir Velchev

16

1) Marc Gravell이 말했듯이 ONE random-generator를 사용해보십시오. 생성자에 System.Environment.TickCount를 추가하는 것이 항상 좋습니다.

2) 하나의 팁. 100 개의 객체를 만들고 각 객체에 자체 랜덤 생성기가 있어야한다고 가정합니다 (매우 짧은 기간에 임의의 숫자의 LOADS를 계산하는 경우 유용합니다). 루프 (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);

건배.


3
루프에서 System.Environment.TickCount를 이동합니다. 반복하는 동안 똑딱 거리면 두 개의 항목이 동일한 시드로 초기화됩니다. 또 다른 옵션은 틱 카운트를 i와 다르게 결합하는 것입니다 (예 : System.Environment.TickCount << 8 + i)
Dolphin

올바르게 이해하면 "System.Environment.TickCount + i"가 동일한 값을 초래할 수 있습니까?
sabiland

편집 : 물론 루프 안에 TickCount를 가질 필요는 없습니다. 내 잘못이야 :).
sabiland

2
기본 Random()생성자는 Random(Environment.TickCount)어쨌든 호출
Alsty

5

실행할 때마다

Random random = new Random (15);

수백만 번 실행해도 중요하지 않으며 항상 동일한 시드를 사용합니다.

당신이 사용하는 경우

Random random = new Random ();

해커가 시드를 추측하고 알고리즘이 시스템의 보안과 관련되어 있으면 알고리즘이 손상되면 다른 임의의 숫자 시퀀스를 얻습니다. 당신은 복수를 실행합니다. 이 생성자에서 시드는 시스템 시계에 의해 지정되며 매우 짧은 시간 (밀리 초) 내에 여러 인스턴스가 생성되면 동일한 시드를 가질 수 있습니다.

안전한 임의의 숫자가 필요한 경우 클래스를 사용해야합니다

System.Security.Cryptography.RNGCryptoServiceProvider

public static int Next(int min, int max)
{
    if(min >= max)
    {
        throw new ArgumentException("Min value is greater or equals than Max value.");
    }
    byte[] intBytes = new byte[4];
    using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        rng.GetNonZeroBytes(intBytes);
    }
    return  min +  Math.Abs(BitConverter.ToInt32(intBytes, 0)) % (max - min + 1);
}

용법:

int randomNumber = Next(1,100);

It does not matter if you execute it millions of times, you will always use the same seed. 씨앗을 직접 지정하지 않으면 사실이 아닙니다.
LarsTech

고정되었습니다. 감사합니다. LarsTech라고 말하듯이 동일한 시드가 항상 지정되면 항상 동일한 난수 시퀀스가 ​​생성됩니다. 내 대답에서는 항상 동일한 시드를 사용하는 경우 매개 변수가있는 생성자를 참조합니다. Random 클래스는 의사 난수 만 생성합니다. 누군가 알고리즘에서 사용한 시드를 찾은 경우 알고리즘의 보안 또는 임의성을 손상시킬 수 있습니다. RNGCryptoServiceProvider 클래스를 사용하면 임의의 숫자를 안전하게 가질 수 있습니다. 이미 수정했습니다. 수정 해 주셔서 감사합니다.
Joma

0

다음과 같이 Random 클래스 변수를 선언하십시오.

    Random r = new Random();
    // ... Get three random numbers.
    //     Here you'll get numbers from 5 to 9
    Console.WriteLine(r.Next(5, 10));

목록에서 매번 다른 난수를 얻으려면 다음을 사용하십시오.

r.Next(StartPoint,EndPoint) //Here end point will not be included

Random r = new Random()한 번 선언하여 매번 .


전화를 걸 때 new Random()시스템 시계가 사용되지만 시계가 변경되기 전에 전체 코드를 연속으로 두 번 호출하면 동일한 난수가 발생합니다. 그것이 위의 답변의 요점입니다.
Savage

-1

많은 해결책이 있습니다. 여기서 하나 : 숫자 만 지우고 싶을 때 메소드는 임의의 결과 길이를받습니다.

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

기본 문제는 여전히 동일합니다. Random인스턴스를 전달하고 있지만 여전히 호출자가 공유 인스턴스를 생성 할 것으로 예상하고 있습니다. 호출자가 매번 새 인스턴스를 만들고 시계가 변경되기 전에 코드가 두 번 실행되면 동일한 임의의 숫자가 표시됩니다. 따라서이 답변은 여전히 ​​잘못된 가정을합니다.
야만인

호출 방법은 구현에 대한 걱정하지 않습니다, 그것은 단지 난수 등을 받고에 관심 - 또한, 임의의 숫자를 생성하는 방법을 데의 요점은 캡슐화
Savage
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.