C #에서 임의의 10 진수 생성
무작위 System.Decimal을 어떻게 얻을 수 있습니까? 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.Decimal은 10을 밑으로하는 부동 소수점입니다) 균일 한 난수의 적절한 정의가 무엇인지 논쟁의 여지가 있습니다.
한편으로 부동 소수점 숫자는 컴퓨터에서 고정 된 수의 비트로 표현되기 때문에 가능한 결과의 수가 한정되어 있음이 분명합니다. 따라서 적절한 분포는 각각의 표현 가능한 숫자가 동일한 확률을 갖는 이산 형 연속 분포라고 주장 할 수 있습니다. 그것은 기본적으로 Jon Skeet 와 John Leidegren의 구현이하는 일입니다.
다른 한편으로는 부동 소수점 숫자가 실수에 대한 근사치로 간주되기 때문에 실제 RNG가 실제 RNG 인 경우에도 연속 난수 생성기의 동작을 근사화하는 것이 더 나을 것이라고 주장 할 수 있습니다. 실제로 이산. 이것은 Random.NextDouble ()에서 얻는 동작입니다. 여기서-0.00001-0.00002 범위에 0.8-0.9 범위에있는 것만 큼 표현 가능한 숫자가 거의 있지만 두 번째 범위의 숫자-예상대로.
따라서 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);
}
기본적으로 스케일 값은 해당 범위의 크기에 비례하여 선택됩니다.
즉, 시간의 90 % 범위에 가능한 범위의 90 %가 포함되어 있기 때문에 시간의 1 9 % 범위 등 0의 90 % 배율을 얻어야합니다.
일부 숫자가 다중 표현을 가지고 있다는 점을 고려하기 때문에 구현에는 여전히 몇 가지 문제가 있습니다. 그러나 다른 구현보다 균일 한 분포에 훨씬 더 가까워 야합니다.
여기에 나를 위해 잘 작동하는 범위 구현이있는 Decimal random이 있습니다.
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;
}
}
편집 : lo, mid 및 hi 주석에서 언급했듯이 int.MaxValue를 포함 할 수 없으므로 전체 Decimals 범위를 사용할 수 없습니다.
여기 있습니다 ... crypt 라이브러리를 사용하여 몇 개의 임의의 바이트를 생성 한 다음 10 진수 값으로 변환합니다 ... 10 진수 생성자는 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);
}
도움이 될 기성 구현에 대해서는 다음 링크를 확인하십시오.
광범위한 분포는 특히 흥미롭고 System.Random에서 직접 파생 된 난수 생성기 (MersenneTwister 등)를 기반으로 구축되었으며 모두 편리한 확장 메서드 (예 : NextFullRangeInt32, NextFullRangeInt64, NextDecimal 등)를 제공합니다. 물론 확장 메서드로 장식 된 단순히 System.Random 인 기본 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
To be honest I don't believe the internal format of the C# decimal works the way many people think. For this reason at least some of the solutions presented here are possibly invalid or may not work consistently. Consider the following 2 numbers and how they are stored in the decimal format:
0.999999999999999m
Sign: 00
96-bit integer: 00 00 00 00 FF 7F C6 A4 7E 8D 03 00
Scale: 0F
and
0.9999999999999999999999999999m
Sign: 00
96-bit integer: 5E CE 4F 20 FF FF FF 0F 61 02 25 3E
Scale: 1C
Take special note of how the scale is different but both values are nearly the same, that is, they are both less than 1 by only a tiny fraction. It appears that it is the scale and the number of digits that have a direct relationship. Unless I'm missing something, this should throw a monkey wrench into most any code that tampers with the 96-bit integer part of a decimal but leaves the scale unchanged.
In experimenting I found that the number 0.9999999999999999999999999999m, which has 28 nines, has the maximum number of nines possible before the decimal will round up to 1.0m.
Further experimenting proved the following code sets the variable "Dec" to the value 0.9999999999999999999999999999m:
double DblH = 0.99999999999999d;
double DblL = 0.99999999999999d;
decimal Dec = (decimal)DblH + (decimal)DblL / 1E14m;
It is from this discovery that I came up with the extensions to the Random class that can be seen in the code below. I believe this code is fully functional and in good working order, but would be glad for other eyes to be checking it for mistakes. I'm not a statistician so I can't say if this code produces a truly uniform distribution of decimals, but if I had to guess I would say it fails perfection but comes extremely close (as in 1 call out of 51 trillion favoring a certain range of numbers).
The first NextDecimal() function should produce values equal to or greater than 0.0m and less than 1.0m. The do/while statement prevents RandH and RandL from exceeding the value 0.99999999999999d by looping until they are below that value. I believe the odds of this loop ever repeating are 1 in 51 trillion (emphasis on the word believe, I don't trust my math). This in turn should prevent the functions from ever rounding the return value up to 1.0m.
The second NextDecimal() function should work the same as the Random.Next() function, only with Decimal values instead of integers. I actually haven't been using this second NextDecimal() function and haven't tested it. Its fairly simple so I think I have it right, but again, I haven't tested it - so you will want to make sure it is working correctly before relying on it.
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;
}
}
I wanted to generate "random" decimals up to 9 decimal places. My approach was to just generate a double and divide it for the decimals.
int randomInt = rnd.Next(0, 100);
double randomDouble = rnd.Next(0, 999999999);
decimal randomDec = Convert.ToDecimal(randomint) + Convert.ToDecimal((randomDouble/1000000000));
the "randomInt" is the number before the decimal place, you could just put 0. To reduce decimal points simply remove "9"s in random and "0"s in dividing
Since the OP question is very embracing and just want a random System.Decimal without any restriction, below is a very simple solution that worked for me.
I was not concerned with any type of uniformity or precision of the generated numbers, so others answers here are probably better if you have some restrictions, but this one works fine in simple cases.
Random rnd = new Random();
decimal val;
int decimal_places = 2;
val = Math.Round(new decimal(rnd.NextDouble()), decimal_places);
In my specific case, I was looking for a random decimal to use as a money string, so my complete solution was:
string value;
value = val = Math.Round(new decimal(rnd.NextDouble()) * 1000,2).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture);
참고URL : https://stackoverflow.com/questions/609501/generating-a-random-decimal-in-c-sharp
'IT박스' 카테고리의 다른 글
Scala 용 정적 분석 도구의 현재 상태는 무엇입니까? (0) | 2020.12.09 |
---|---|
CSS만으로 사전에 줄 번호 만들기 (0) | 2020.12.09 |
양식 인증 시간 초과와 세션 시간 초과 (0) | 2020.12.09 |
자바 플러그인 프레임 워크 선택 (0) | 2020.12.09 |
PDO에서 오류 메시지를 압축하는 방법은 무엇입니까? (0) | 2020.12.09 |