[C#] 난수 생성기는 하나의 난수 만 생성

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

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

왜 그런 일이 발생합니까?



답변

할 때마다 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());

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


답변

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

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


답변

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

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


답변

내 대답은 여기에서 :

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

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

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


답변

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

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


답변

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

건배.


답변

실행할 때마다

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