Как я могу получить случайную систему.Decimal? System.Random
не поддерживает его напрямую.Создание случайного десятичного числа в C#
ответ
EDIT: Удалена старая версия
Это похоже на версию Даниила, но даст полный диапазон. Он также вводит новый метод расширения, чтобы получить случайное значение «любое целое число», которое, по моему мнению, удобно.
Обратите внимание, что распределение десятичных знаков здесь не равно.
/// <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);
}
Я задавался вопросом об этом и/или ctor, который берет байт [] - но все ли байты [] перестановки легальны? –
@Marc: Мое воспоминание о том, что они есть, а остальные биты просто игнорируются. Я еще не проверил. –
Я сделал это, он просто прошел через частный ctor, как если бы он был законным. Вы должны попробовать это и посмотреть, имеет ли это смысл. Реализация скрыта (InternalCall), и эти данные могут испортить вычисления. –
Я немного озадачил это. Это лучшее, что я мог придумать:
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;
}
}
Edit: Как было отмечено в комментариях Ио, середине и привет никогда не может содержать int.MaxValue поэтому полный спектр Десятичные не представляется возможным.
Что будет делать это ... –
Не совсем ... Random.Next (int.MinValue, int.MaxValue) никогда не вернет int.MaxValue. У меня есть ответ, но я думаю, что я могу улучшить его. –
Статистика не моя сильная сторона, поэтому я, вероятно, ошибаюсь, но я бы волновался, что распространение может быть не очень однородным. –
здесь вы идете ... использует библиотеку склеп, чтобы генерировать несколько случайных байт, то convertes их десятичного значения ... см MSDN for the decimal constructor
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);
}
Это означает, что мы ограничены 65536 значениями из огромного возможного диапазона, хотя, не так ли? –
да, да. :-( –
Мы живем во времена 16-разрядных вычислений? В чем смысл этого? –
Вы обычно ожидаете от случайных чисел, генератор т он не только генерировал случайные числа, но и то, что числа были равномерно распределены случайным образом.
Существует два определения равномерно случайных: discrete uniformly random и continuous uniformly random.
Дискретно равномерное случайное имеет смысл для генератора случайных чисел, который имеет конечное число различных возможных результатов. Например, генерируя целое число от 1 до 10. Тогда вы ожидаете, что вероятность получения 4 будет такой же, как получение 7.
Непрерывное равномерное случайное значение имеет смысл, когда генератор случайных чисел генерирует числа в диапазоне. Например, генератор, который генерирует реальное число между 0 и 1. Тогда вы ожидаете, что вероятность получить число от 0 до 0,5 будет таким же, как получение числа от 0,5 до 1.
Когда генератор случайных чисел генерирует числа с плавающей запятой (это в основном то, что System.Decimal - это просто плавающая точка, база 10), можно утверждать, что правильное определение равномерно случайного:
С одной стороны, поскольку плавающие номер точки представляется фиксированным числом бит в компьютере, очевидно, что существует конечное число возможных результатов. Таким образом, можно утверждать, что правильное распределение является дискретным непрерывным распределением с каждым представимым числом, имеющим ту же вероятность. Это в основном то, что Jon Skeet's и John Leidegren's осуществление.
С другой стороны, можно утверждать, что, поскольку число с плавающей запятой должно быть приближением к вещественному числу, нам было бы лучше, если бы попытались аппроксимировать поведение непрерывного генератора случайных чисел, даже если фактические RNG на самом деле дискретны. Это поведение, которое вы получаете от Random.NextDouble(), где - хотя в диапазоне 0,00001-0,00002 имеется примерно столько же числовых чисел, сколько есть в диапазоне 0.8-0.9, вы в тысячу раз чаще получаете число во втором диапазоне - как и вы ожидать.
Так что правильная реализация Random.NextDecimal() должна, вероятно, быть равномерно распределенной.
Вот простой вариант ответа Джона Скита, равномерно распределены между 0 и 1 (я повторно его NextInt32) метод расширения():
public static decimal NextDecimal(this Random rng)
{
return new decimal(rng.NextInt32(),
rng.NextInt32(),
rng.Next(0x204FCE5E),
false,
0);
}
Вы также могли бы обсудить, как получить равномерное распределение весь диапазон десятичных знаков. Существует, вероятно, более простой способ сделать это, но это небольшая модификация John Leidegren's answer должна производить относительно равномерное распределение:
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% возможного диапазона - масштаб 1 9% времени и т.д.
Есть еще некоторые проблемы с реализацией, поскольку он учитывает, что некоторые числа имеют множественные представления, но они должны быть намного ближе к равномерному распределению, чем другие реализации.
Почему ваше соотношение масштаба 0,1? Это не похоже на меня. Возможно, 1.0/28 более равномерна. –
И как это отличается от '28 * r.NextDouble()'? –
Это довольно интересно, потому что мы просто просто вставляем мусор. Предположительно, вы бы хотели иметь хорошее равномерное распределение между 0 и 1, а затем вы бы масштабировали это с помощью чего хотя я не понимаю, как это достигается? –
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
Здесь Decimal random с реализацией Range, которая отлично работает для меня.
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);
}
Заканчивать следующую ссылку для готовых реализаций, которые должны помочь:
MathNet.Numerics, Random Numbers and Probability Distributions
Обширные распределения особенно интересен, построенный на вершине генераторов случайных чисел (Вихрь Мерсенна, и т.д. .), непосредственно полученный из System.Random, все из которых предоставляют удобные методы расширения (например, NextFullRangeInt32, NextFullRangeInt64, NextDecimal и т. д.). Вы можете, конечно, просто использовать SystemRandomSource по умолчанию, который просто System.Random украшен методами расширения.
О, и вы можете создать свои экземпляры RNG как потокобезопасные, если вам это нужно.
Очень удобно!
Это старый вопрос, но для тех, кто просто его читает, зачем изобретать колесо?
Я знаю, что это старый вопрос, но distribution issue Rasmus Faber described продолжал беспокоить меня, поэтому я придумал следующее. Я не заглядывал в глубину на NextInt32 implementation provided by Jon Skeet, и я предполагаю (надеясь), что он имеет то же распределение, что и 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);
}
Если честно, я не считаю, что внутренний формат десятичного числа C# работает так, как думают многие. По этой причине хотя бы некоторые из представленных здесь решений могут быть недействительными или могут не работать последовательно.Рассмотрим следующие 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, только крошечной фракцией. Похоже, что это масштаб и число цифр, которые имеют прямую связь. Если я чего-то не упускаю, это должно бросить ключ обезьяны в любой код, который забивает 96-битную целочисленную часть десятичной дроби, но оставляет неизменным масштаб.
В эксперименте я обнаружил, что число 0.999999999999999999999999999999m, которое имеет 28 девяток, имеет максимальное количество девяток возможных до того, как десятичное число будет округлено до 1,0 м.
Далее экспериментируя доказал следующий код устанавливает переменную «Dec» к значению 0.9999999999999999999999999999m:
double DblH = 0.99999999999999d;
double DblL = 0.99999999999999d;
decimal Dec = (decimal)DblH + (decimal)DblL/1E14m;
Именно из этого открытия, что я пришел с расширениями к Random класса, которые можно увидеть в код ниже. Я считаю, что этот код полностью функциональный и в хорошем рабочем состоянии, но был бы рад, если бы другие глаза проверяли его на наличие ошибок. Я не статистик, поэтому я не могу сказать, действительно ли этот код дает поистине равномерное распределение десятичных знаков, но если бы я должен был догадаться, я бы сказал, что он не соответствует совершенству, но очень близок (как в 1 призыве из 51 триллиона в пользу определенный диапазон чисел).
Первая функция NextDecimal() должна выдавать значения, равные или превышающие 0,0 м и менее 1,0 м. Оператор do/while не позволяет RandH и RandL превышать значение 0.99999999999999d путем циклизации до тех пор, пока они не станут ниже этого значения. Я считаю, что шансы этой петли повторяются в 51 триллионе (акцент на слово верят, я не верю своей математике). Это, в свою очередь, должно препятствовать функциям от округления возвращаемого значения до 1,0 м.
Вторая функция 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;
}
}
Кроме того, благодаря силе легкого материала, чтобы сделать:
var rand = new Random();
var item = new decimal(rand.NextDouble());
Я хотел произвести «случайные» десятичные до 9 знаков после запятой. Мой подход состоял в том, чтобы просто создать двойник и разделить его на десятичные числа.
int randomInt = rnd.Next(0, 100);
double randomDouble = rnd.Next(0, 999999999);
decimal randomDec = Convert.ToDecimal(randomint) + Convert.ToDecimal((randomDouble/1000000000));
«randomInt» это число до десятичного знака, вы можете просто положить 0. Для уменьшения десятичных точек просто удалить «9» ы в случайном и «0» в делящихся
С вопрос OP очень обнимает и просто хочет случайную System.Decimal без каких-либо ограничений, ниже это очень простое решение, которое сработало для меня.
Меня не интересовали какие-либо однородности или точности сгенерированных чисел, поэтому другие ответы здесь, вероятно, лучше, если у вас есть какие-то ограничения, но это хорошо работает в простых случаях.
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);
бы не только легче сгенерировать случайное число между , скажем, 1 и 999, и разделите результат на 100? ех. случайное число 1 будет 0,01 и 999 будет 9.99 –
@StefanZCamilleri Зависит от того, для чего вам нужен случайный десятичный знак. Прочитайте некоторые из приведенных ниже ответов. Полный диапазон возможных десятичных значений, которые могут быть представлены, действительно большой, и требуется некоторое представление, чтобы получить случайные ints для подачи в конструктор Decimal. Тогда возникает вопрос о том, насколько однородным является случайное распределение порожденных десятичных значений. –
Кроме того, если я не ошибаюсь, .01 не находится между 1 и 999. Это меньше 1, поэтому не будет действительного ответа. –