문제

임의의 시스템을 어떻게 얻을 수 있습니까? System.Random 직접 지원하지 않습니다.

도움이 되었습니까?

해결책

편집 : 이전 버전을 제거했습니다

이것은 Daniel의 버전과 유사하지만 전체 범위를 제공합니다. 또한 무작위 "모든 정수"값을 얻기 위해 새로운 확장 방법을 도입합니다.

소수의 분포는 여기에 있습니다 균일하지 않습니다.

/// <summary>
/// Returns an Int32 with a random value across the entire range of
/// possible values.
/// </summary>
public static int NextInt32(this Random rng)
{
     int firstBits = rng.Next(0, 1 << 4) << 28;
     int lastBits = rng.Next(0, 1 << 28);
     return firstBits | lastBits;
}

public static decimal NextDecimal(this Random rng)
{
     byte scale = (byte) rng.Next(29);
     bool sign = rng.Next(2) == 1;
     return new decimal(rng.NextInt32(), 
                        rng.NextInt32(),
                        rng.NextInt32(),
                        sign,
                        scale);
}

다른 팁

당신은 일반적으로 임의의 수 여자로부터 임의의 숫자를 생성 할뿐만 아니라 숫자가 균일하게 무작위로 생성 될 것으로 기대합니다.

균일하게 무작위에 대한 두 가지 정의가 있습니다. 균일하게 무작위로 이산됩니다 그리고 연속 균일하게 무작위.

유한 한 수의 다른 가능한 결과를 갖는 임의의 숫자 생성기에 대해서는 균일하게 무작위로 무작위로 의미가 있습니다. 예를 들어 1과 10 사이의 정수를 생성하는 것과 같은 경우 4를 얻을 확률은 7을 얻는 것과 동일합니다.

임의의 숫자 생성기가 범위에서 숫자를 생성 할 때 연속적으로 균일하게 무작위로 의미가 있습니다. 예를 들어 0과 1 사이의 실수 숫자를 생성하는 생성기는 0에서 0.5 사이의 숫자를 얻을 확률이 0.5에서 1 사이의 숫자를 얻는 것과 동일 할 것으로 예상합니다.

임의의 숫자 생성기가 부동 소수점 숫자를 생성 할 때 (기본적으로 System.

한편으로, 부동 소수점 번호는 컴퓨터에서 고정 된 수의 비트로 표시되기 때문에 가능한 결과가 유한 한 것으로 나타납니다. 따라서 적절한 분포는 각각의 대표 숫자가 동일한 확률을 갖는 개별 연속 분포라고 주장 할 수 있습니다. 그것은 기본적으로 무엇입니다 Jon Skeet 's 그리고 John Leidegren 's 구현.

다른 한편으로, 부동 소수점 수는 실수에 대한 근사치가되어야한다고 주장 할 수 있으므로 실제 RNG이지만 연속 임의 숫자 생성기의 동작을 근사화하려고 시도하여 더 나을 것이라고 주장 할 수 있습니다. 실제로 이산. 이것은 당신이 random.nextdouble ()에서 얻는 동작입니다. 두 번째 범위의 숫자 - 예상대로.

따라서 Random.nextDecimal ()의 적절한 구현은 아마도 지속적으로 균일하게 분포되어야합니다.

다음은 0과 1 사이에 균일하게 분포 된 Jon Skeet의 답변의 간단한 변형입니다 (그의 NextInt32 () 확장 방법) :

public static decimal NextDecimal(this Random rng)
{
     return new decimal(rng.NextInt32(), 
                        rng.NextInt32(),
                        rng.Next(0x204FCE5E),
                        false,
                        0);
}

또한 전체 범위의 소수점에서 균일 한 분포를 얻는 방법에 대해 논의 할 수도 있습니다. 이 작업을 수행하는 더 쉬운 방법이있을 수 있지만이 약간의 수정은 John Leidegren의 대답 비교적 균일 한 분포를 생성해야합니다.

private static int GetDecimalScale(Random r)
{
  for(int i=0;i<=28;i++){
    if(r.NextDouble() >= 0.1)
      return i;
  }
  return 0;
}

public static decimal NextDecimal(this Random r)
{
    var s = GetDecimalScale(r);
    var a = (int)(uint.MaxValue * r.NextDouble());
    var b = (int)(uint.MaxValue * r.NextDouble());
    var c = (int)(uint.MaxValue * r.NextDouble());
    var n = r.NextDouble() >= 0.5;
    return new Decimal(a, b, c, n, s);
}

기본적으로, 우리는 스케일 값이 해당 범위의 크기에 비례하여 선택되도록합니다.

즉, 우리는 시간의 0 90%의 척도를 얻어야한다는 것을 의미합니다.이 범위는 가능한 범위의 90%를 포함하기 때문에 시간의 9% 척도 등이 있기 때문입니다.

일부 숫자는 여러 표현이 있다는 점을 고려하기 때문에 구현에는 여전히 문제가 있습니다. 그러나 다른 구현보다 균일 한 분포에 훨씬 가깝습니다.

다음은 나에게 잘 작동하는 범위 구현의 소수 무작위입니다.

public static decimal NextDecimal(this Random rnd, decimal from, decimal to)
{
    byte fromScale = new System.Data.SqlTypes.SqlDecimal(from).Scale;
    byte toScale = new System.Data.SqlTypes.SqlDecimal(to).Scale;

    byte scale = (byte)(fromScale + toScale);
    if (scale > 28)
        scale = 28;

    decimal r = new decimal(rnd.Next(), rnd.Next(), rnd.Next(), false, scale);
    if (Math.Sign(from) == Math.Sign(to) || from == 0 || to == 0)
        return decimal.Remainder(r, to - from) + from;

    bool getFromNegativeRange = (double)from + rnd.NextDouble() * ((double)to - (double)from) < 0;
    return getFromNegativeRange ? decimal.Remainder(r, -from) + from : decimal.Remainder(r, to);
}

나는 이것이 오래된 질문이라는 것을 알고 있지만 분포 문제 Rasmus Faber가 설명했습니다 계속 나를 괴롭히서 다음을 생각해 냈습니다. 나는 깊이를 보지 않았다 Jon Skeet이 제공 한 NextInt32 구현 그리고 (희망)와 동일한 분포를 가정합니다. random.next ().

//Provides a random decimal value in the range [0.0000000000000000000000000000, 0.9999999999999999999999999999) with (theoretical) uniform and discrete distribution.
public static decimal NextDecimalSample(this Random random)
{
    var sample = 1m;
    //After ~200 million tries this never took more than one attempt but it is possible to generate combinations of a, b, and c with the approach below resulting in a sample >= 1.
    while (sample >= 1)
    {
        var a = random.NextInt32();
        var b = random.NextInt32();
        //The high bits of 0.9999999999999999999999999999m are 542101086.
        var c = random.Next(542101087);
        sample = new Decimal(a, b, c, false, 28);
    }
    return sample;
}

public static decimal NextDecimal(this Random random)
{
    return NextDecimal(random, decimal.MaxValue);
}

public static decimal NextDecimal(this Random random, decimal maxValue)
{
    return NextDecimal(random, decimal.Zero, maxValue);
}

public static decimal NextDecimal(this Random random, decimal minValue, decimal maxValue)
{
    var nextDecimalSample = NextDecimalSample(random);
    return maxValue * nextDecimalSample + minValue * (1 - nextDecimalSample);
}

또한 쉬운 것들의 힘을 통해 다음과 같습니다.

var rand = new Random();
var item = new decimal(rand.NextDouble());

나는 이것을 조금 당황했다. 이것은 내가 생각해 낼 수있는 최선입니다.

public class DecimalRandom : Random
    {
        public override decimal NextDecimal()
        {
            //The low 32 bits of a 96-bit integer. 
            int lo = this.Next(int.MinValue, int.MaxValue);
            //The middle 32 bits of a 96-bit integer. 
            int mid = this.Next(int.MinValue, int.MaxValue);
            //The high 32 bits of a 96-bit integer. 
            int hi = this.Next(int.MinValue, int.MaxValue);
            //The sign of the number; 1 is negative, 0 is positive. 
            bool isNegative = (this.Next(2) == 0);
            //A power of 10 ranging from 0 to 28. 
            byte scale = Convert.ToByte(this.Next(29));

            Decimal randomDecimal = new Decimal(lo, mid, hi, isNegative, scale);

            return randomDecimal;
        }
    }

편집 : 주석에 언급 된 바와 같이, Mid 및 Hi에는 int.maxvalue를 포함 할 수 없으므로 전체 범위의 소수점이 불가능합니다.

여기에가는 ... Crypt 라이브러리를 사용하여 몇 개의 임의 바이트를 생성 한 다음 소수점 값으로 변환합니다 ... 소수점 생성자의 MSDN

using System.Security.Cryptography;

public static decimal Next(decimal max)
{
    // Create a int array to hold the random values.
    Byte[] randomNumber = new Byte[] { 0,0 };

    RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();

    // Fill the array with a random value.
    Gen.GetBytes(randomNumber);

    // convert the bytes to a decimal
    return new decimal(new int[] 
    { 
               0,                   // not used, must be 0
               randomNumber[0] % 29,// must be between 0 and 28
               0,                   // not used, must be 0
               randomNumber[1] % 2  // sign --> 0 == positive, 1 == negative
    } ) % (max+1);
}

더 나은 범위의 숫자를 제공하기 위해 다른 소수점 생성자를 사용하도록 수정되었습니다.

public static decimal Next(decimal max)
{
    // Create a int array to hold the random values.
    Byte[] bytes= new Byte[] { 0,0,0,0 };

    RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();

    // Fill the array with a random value.
    Gen.GetBytes(bytes);
    bytes[3] %= 29; // this must be between 0 and 28 (inclusive)
    decimal d = new decimal( (int)bytes[0], (int)bytes[1], (int)bytes[2], false, bytes[3]);

        return d % (max+1);
    }

도움이 될 기성품 구현에 대한 다음 링크를 확인하십시오.

Mathnet.numerics, 랜덤 숫자 및 확률 분포

광범위한 분포는 특히 관심이 있으며, 랜덤에서 직접 파생 된 임의의 숫자 생성기 (Mersennetwister 등) 위에 구축되며, 모두 편리한 확장 방법을 제공합니다 (예 : NextfullRangeInt32, NextFullRangeInt64, NextDecimal 등). 물론 단순히 시스템 인 기본 SystemRandomsource를 사용할 수 있습니다.

아, 그리고 필요한 경우 RNG 인스턴스를 스레드 안전으로 만들 수 있습니다.

참으로 매우 편리합니다!

이것은 오래된 질문이지만 방금 읽는 사람들에게 왜 바퀴를 재발 명합니까?

static decimal GetRandomDecimal()
    {

        int[] DataInts = new int[4];
        byte[] DataBytes = new byte[DataInts.Length * 4];

        // Use cryptographic random number generator to get 16 bytes random data
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        do
        {
            rng.GetBytes(DataBytes);

            // Convert 16 bytes into 4 ints
            for (int index = 0; index < DataInts.Length; index++)
            {
                DataInts[index] = BitConverter.ToInt32(DataBytes, index * 4);
            }

            // Mask out all bits except sign bit 31 and scale bits 16 to 20 (value 0-31)
            DataInts[3] = DataInts[3] & (unchecked((int)2147483648u | 2031616));

          // Start over if scale > 28 to avoid bias 
        } while (((DataInts[3] & 1835008) == 1835008) && ((DataInts[3] & 196608) != 0));

        return new decimal(DataInts);
    }
    //end

솔직히 말해서 나는 C# 10 진수의 내부 형식이 많은 사람들이 생각하는 방식으로 작동한다고 믿지 않습니다. 이러한 이유로 적어도 여기에 제시된 솔루션 중 일부는 유효하지 않거나 일관되게 작동하지 않을 수 있습니다. 다음 2 개의 숫자와 소수점 형식으로 저장되는 방법을 고려하십시오.

0.999999999999999m
Sign: 00
96-bit integer: 00 00 00 00 FF 7F C6 A4 7E 8D 03 00
Scale: 0F

그리고

0.9999999999999999999999999999m
Sign: 00
96-bit integer: 5E CE 4F 20 FF FF FF 0F 61 02 25 3E
Scale: 1C

스케일이 어떻게 다른지에 대해 특별히 주목하지만 두 값은 거의 동일합니다. 즉, 작은 분수만으로는 모두 1보다 작습니다. 직접적인 관계가있는 규모와 숫자 수인 것으로 보입니다. 내가 뭔가를 놓치지 않는 한, 이것은 10 진수의 96 비트 정수 부분을 가진 대부분의 코드에 원숭이 렌치를 던져야하지만 스케일은 변하지 않게합니다.

실험에서 나는 숫자 0.999999999999999999999999999m (28 개)을 가진 수는 10 진수가 1.0m까지 반올림하기 전에 가능한 최대 9 개 수를 가지고 있음을 발견했다.

추가 실험은 다음 코드가 변수 "Dec"를 0.99999999999999999999999999M으로 설정 한 것으로 입증되었습니다.

double DblH = 0.99999999999999d;
double DblL = 0.99999999999999d;
decimal Dec = (decimal)DblH + (decimal)DblL / 1E14m;

이 발견에서 아래 코드에서 볼 수있는 임의의 클래스에 대한 확장을 생각해 냈습니다. 나는이 코드가 완전히 작동하고 잘 작동하는 순서라고 생각하지만 다른 눈이 실수를 확인하는 것이 기뻐할 것입니다. 나는 통계 학자가 아니기 때문에이 코드가 진정으로 균일 한 소수의 분포를 생성하는지 말할 수는 없지만, 내가 추측해야한다면 완벽하게 실패하지만 매우 가까워 질 것입니다 (51 조 5 조 중 1 조는 특정 숫자 범위).

첫 번째 NEXTDECIMAL () 함수는 0.0m 이상의 값을 1.0m 미만으로 생성해야합니다. Do/While 진술은 Randh와 Randl이 해당 값 미만일 때까지 반복하여 0.999999999999999D를 초과하는 것을 방지합니다. 나는이 루프가 반복되는 확률이 51 조 중 1 조차인 것으로 믿는다 (믿음에 중점을 두어, 나는 내 수학을 믿지 않는다). 이로 인해 함수가 리턴 값을 1.0m까지 반올림하는 것을 방지해야합니다.

두 번째 NextDecimal () 함수는 random.next () 함수와 동일하게 작동하며 정수 대신 소수점 값으로 만 작동해야합니다. 실제로이 두 번째 NextDecimal () 함수를 사용하지 않았으며 테스트하지 않았습니다. 상당히 간단하므로 제대로 생각하지만 다시 테스트하지 않았습니다. 따라서 의존하기 전에 올바르게 작동하는지 확인하고 싶습니다.

public static class ExtensionMethods {
    public static decimal NextDecimal(this Random rng) {
        double RandH, RandL;
        do {
            RandH = rng.NextDouble();
            RandL = rng.NextDouble();
        } while((RandH > 0.99999999999999d) || (RandL > 0.99999999999999d));
        return (decimal)RandH + (decimal)RandL / 1E14m;
    }
    public static decimal NextDecimal(this Random rng, decimal minValue, decimal maxValue) {
        return rng.NextDecimal() * (maxValue - minValue) + minValue;
    }
}

나는 최대 9 자리까지 "무작위"소수를 생성하고 싶었습니다. 저의 접근 방식은 단지 이중을 생성하고 소수를 위해 나누는 것이 었습니다.

int randomInt = rnd.Next(0, 100);

double randomDouble = rnd.Next(0, 999999999);
decimal randomDec = Convert.ToDecimal(randomint) + Convert.ToDecimal((randomDouble/1000000000));

"randomint"는 소수점 이하의 숫자이며, 10 진수를 줄일 수 있습니다. 소수점을 줄이기 위해 단순히 "9"를 무작위로 제거하고 "0"은 나누기

OP 질문은 매우 수용하고 무작위 시스템을 원하기 때문에 제한이없는 임의의 시스템을 원하기 때문에 아래는 저에게 효과적인 매우 간단한 솔루션입니다.

생성 된 숫자의 균일 성이나 정밀도에 대해서는 걱정하지 않았으므로 제한 사항이 있으면 여기에 다른 답변이 더 좋습니다. 그러나 간단한 경우에는 잘 작동합니다.

Random rnd = new Random();
decimal val;
int decimal_places = 2;
val = Math.Round(new decimal(rnd.NextDouble()), decimal_places);

내 특정한 경우, 나는 돈 줄로 사용할 무작위 소수점을 찾고 있었기 때문에 내 완전한 해결책은 다음과 같습니다.

string value;
value = val = Math.Round(new decimal(rnd.NextDouble()) * 1000,2).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top