سؤال

كيف يمكنني الحصول على System.Decimal عشوائي؟ System.Random لا يدعمها بشكل مباشر

هل كانت مفيدة؟

المحلول

يحرر:تمت إزالة النسخة القديمة

هذا مشابه لنسخة دانيال، لكنه سيعطي النطاق الكامل.كما يقدم أيضًا طريقة تمديد جديدة للحصول على قيمة عشوائية "لأي عدد صحيح"، والتي أعتقد أنها مفيدة.

لاحظ أن توزيع الكسور العشرية هنا ليست موحدة.

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

نصائح أخرى

هل تتوقع عادة من عشوائي رقم مولد أنه لا تتولد إلا أرقام عشوائية، إلا أن الأرقام كانت موحدة بشكل عشوائي.

وهناك نوعان من تعريفات عشوائية بشكل موحد: منفصلة عشوائية بشكل موحد و< وأ href = "http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)" يختلط = "نوفولو noreferrer"> مستمرة عشوائي موحد .

وبتحفظ عشوائي موحد المنطقي لمولد الأرقام العشوائية التي لديها عدد محدود من النتائج المحتملة المختلفة. على سبيل المثال توليد عدد صحيح بين 1 و 10. أنت عندئذ نتوقع أن احتمال الحصول على 4 هو نفس الحصول على 7.

وباستمرار عشوائي موحد من المنطقي عندما يكون عدد مولد عشوائي يولد أرقام في مجموعة. على سبيل المثال مولد يولد العدد الحقيقي بين 0 و 1. أنت ثم يتوقع أن احتمال الحصول على رقم بين 0 و 0.5 هو نفس الحصول على عدد يتراوح بين 0.5 و 1.

عند يولد عدد المولدات عشوائي أرقام الفاصلة العائمة (والذي هو في الأساس ما هو System.Decimal - انها مجرد الفاصلة العائمة التي قاعدة 10)، فإنه يمكن القول ما تعريف مناسب من عشوائية بشكل موحد هو:

في يد واحدة، لأن عدد الفاصلة العائمة يتم تمثيله من قبل عدد محدد من البتات في الكمبيوتر، فمن الواضح أن هناك عدد محدود من النتائج المحتملة. لذلك يمكن القول أن التوزيع السليم هو توزيع المستمر منفصلة مع كل عدد للتمثيل لها نفس الاحتمال. وهذا هو أساسا ما جون السكيت في و <لأ href = " https://stackoverflow.com/questions/609501/generating-a-random-decimal-in-c/609569#609569">John تنفيذ Leidegren في لا.

من ناحية أخرى، يمكن للمرء أن يجادل بأن منذ يفترض أن يكون تقريبي لعدد حقيقي، وسنكون أفضل حالا من خلال محاولة لتقريب سلوك مستمر مولد رقم عشوائي عدد الفاصلة العائمة - على الرغم من هي RNG الفعلي هو في الواقع منفصلة. هذا هو السلوك الذي تحصل عليه من Random.NextDouble ()، حيث - على الرغم من أن هناك ما يقرب من أكبر عدد ممكن من الأرقام للتمثيل في نطاق 0،00001-0،00002 كما أن هناك في نطاق 0،8-0،9، كنت ألف مرة أكثر احتمالا للحصول على رقم في نطاق الثاني - كما تتوقعون

وحتى التنفيذ السليم للRandom.NextDecimal () ربما ينبغي باستمرار موزعة بشكل متجانس.

وهنا هو الاختلاف البسيط للإجابة جون السكيت والتي يتم توزيعها بشكل موحد بين 0 و 1 (I إعادة له NextInt32 () طريقة التمديد):

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

ويمكنك أيضا مناقشة كيفية الحصول على توزيع موحد على مجموعة كاملة من الكسور العشرية. هناك على الأرجح أسهل طريقة للقيام بذلك، ولكن هذا تعديل طفيف من الإجابة جون 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٪ من نطاق ممكن - مقياس من 1 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);
}

وأعرف أن هذا هو السؤال القديم، ولكن القضية توزيع راسموس فابر وصف أبقى يزعجني ذلك خطرت لي الأتى. أنا لم ينظر بعمق في تنفيذ 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;
        }
    }

وتحرير: وكما لوحظ في التعليقات الصغرى، المتوسطة ومرحبا لا يمكن أبدا أن يحتوي على int.MaxValue ذلك مجموعة كاملة من الكسور العشرية غير ممكن

وهنا تذهب ... يستخدم المكتبة سرداب لتوليد بضع بايت عشوائية، ثم convertes لهم قيمة عشرية ... انظر> وأ href = "http://msdn.microsoft.com/en-us /library/aa326746(VS.71).aspx "يختلط =" نوفولو noreferrer "> 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، الخ) المستمدة مباشرة من System.Random، عن توفير طرق الإرشاد يدوية (مثل NextFullRangeInt32، NextFullRangeInt64، NextDecimal، وما إلى ذلك). يمكنك، بالطبع، مجرد استخدام SystemRandomSource الافتراضي، وهو ببساطة System.Random منمق مع طرق الإرشاد.

وأوه، ويمكنك إنشاء مثيلات 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 # عشري يعمل بالطريقة يظن كثير من الناس. لهذا السبب على الأقل بعض الحلول المقدمة هنا هي ربما غير صالح أو قد لا تعمل باستمرار. النظر في الأرقام 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.9999999999999999999999999999m العدد، الذي لديه 28 ضعيته، لديه الحد الأقصى لعدد ضعيته الممكنة قبل عشري ومحاصرة ل1.0M.

ثبت

وعلاوة على ذلك تجارب رمز التالي بتعيين متغير "ديسمبر" إلى 0.9999999999999999999999999999m القيمة:

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

ومن هذا الاكتشاف أن خطرت لي ملحقات إلى فئة عشوائية والتي يمكن مشاهدتها في التعليمات البرمجية أدناه. أعتقد هذا الرمز تعمل بكامل طاقتها وفي حالة جيدة، ولكن سيكون سعيد للعيون أخرى ليتم فحص ذلك عن الأخطاء. أنا لست إحصائي لذلك لا أستطيع أن أقول إذا ينتج هذا الرمز توزيع موحد حقا من الكسور العشرية، ولكن إذا كان لي أن أخمن أود أن أقول أنه فشل الكمال ولكن يقترب للغاية (كما في 1 دعوة من 51000000000000 المؤيد ل نطاق معين من الأرقام).

ويجب أن ينتج الدالة الأول NextDecimal () قيم مساوية أو أكبر من 0.0M وأقل من 1.0M. افعل / الوقت الذي يمنع فيه بيان RandH وRandL من تجاوز قيمة 0.99999999999999d بواسطة حلقات حتى يتم أدناه تلك القيمة. وأعتقد أن احتمالات تكرار هذه الحلقة من أي وقت مضى هي 1 في 51000000000000 (التشديد على كلمة ويعتقد، أنا لا أثق الرياضيات بلدي). وهذا بدوره يجب منع وظائف من التقريب من أي وقت مضى قيمة الإرجاع إلى 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" هو الرقم قبل منزلة عشرية، هل يمكن أن مجرد وضع 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);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top