c#随机索引到数组的最快方法

时间:2022-12-21 13:29:25

I have an array of double values "vals", I need to randomly index into this array and get a value. GenRandomNumber() returns a number between 0 and 1 but never 0 or 1. I am using Convert.ToInt32 to basically get everything to the left of my decimal place, but there must be a more efficient way of doing this?

我有一个双值“vals”数组,我需要随机索引到这个数组并获得一个值。 GenRandomNumber()返回一个介于0和1之间但从不为0或1的数字。我使用Convert.ToInt32基本上得到了我的小数点左边的所有内容,但是必须有一种更有效的方法吗?

Here's my code:

这是我的代码:

public double GetRandomVal()
{
   int z = Convert.ToInt32(GenRandomNumber() * (vals.Length));
   return vals[z];
}

Thanks

Update

Thanks to all those who have replied, but I am constrained to use a supplied MersenneTwister random number implementation that has method rand.NextDouble()

感谢所有回复的人,但我不得不使用提供方法rand.NextDouble()的MersenneTwister随机数实现

Update 2

Thinking about this some more, all I need to do is gen a random number between 0 and array.length-1 and then use that to randomly index into the array. vals length is 2^20 = 1048576 so generating a random int is sufficient. I notice my MersenneTwister has a method:

再考虑一下,我需要做的就是生成0和array.length-1之间的随机数,然后使用它随机索引到数组中。 vals长度为2 ^ 20 = 1048576,因此生成随机int就足够了。我注意到我的MersenneTwister有一个方法:

public int Next(int maxValue)

If I call it like vals[rand.Next(vals.length-1)] that should do it right? I also see the MersenneTwister has a constructor:

如果我把它称为vals [rand.Next(vals.length-1)]那应该做对吗?我也看到MersenneTwister有一个构造函数:

public MersenneTwister(int[] init)

Not sure what this is for, can I use this to prepopulate the acceptable random numbers for which I provide an array of 0 to vals.length?

不知道这是为了什么,我可以用它来预先填充我提供0到vals.length数组的可接受的随机数吗?

FYI vals is a double array of length 1048576 partitioning the normal distribution curve. I am basically using this mechanism to create Normally distributed numbers as fast as possible, the monte carlo simulation uses billions of Normally distributed random numbers every day so every little bit helps.

FYI vals是一个长度为1048576的双重数组,用于划分正态分布曲线。我基本上使用这种机制来尽可能快地创建正态分布的数字,monte carlo模拟每天使用数十亿个正态分布的随机数,所以每一点都有帮助。

8 个解决方案

#1


Try using a random integer instead:

请尝试使用随机整数:

Random random = new Random();
int randomNumber = random.Next(0, vals.Length);
return vals[randomNumber];

#2


return vals[rng.Next(vals.Length)];

Where rng is

rng是哪里

Random rng = new Random();

#3


I think you have the simplest most direct implementation already identified.

我认为你已经确定了最简单,最直接的实现。

But if you are looking for performance gains in your random indexing algorithm, you may be able to just 'crack' the IEEE 754 encoded double into its exponent and fraction - and use the fraction modulo the array size as a random index.

但是,如果您正在寻找随机索引算法中的性能提升,您可以将IEEE 754编码的双重“破解”为其指数和分数 - 并使用模数作为随机索引模数。

This technique IS NOT likely to be cryptographically secure - so if that is a consideration - don't do it.

这种技术不太可能是加密安全的 - 所以如果这是一个考虑因素 - 不要这样做。

Also, this approach DOES NOT make the code more obvious - I would stick with your original implementation unless maximizing performance is the consideration. As an aside, the slowest part of this processing is most likely the Mersenne Twister generation of random numbers.

此外,这种方法不会使代码更明显 - 我会坚持你的原始实现,除非考虑最大化性能。顺便说一下,这个处理过程中最慢的部分很可能是Mersenne Twister随机数。

Here's the code:

这是代码:

[StructLayout(LayoutKind.Explicit)] // used create a union of Long and Double
public struct IEEE754
{
    private const ulong SIGN_BITS     = 0x8000000000000000;
    private const ulong EXPONENT_BITS = 0x7FF0000000000000;
    private const ulong FRACTION_BITS = 0x000FFFFFFFFFFFFF;

    private const int SIGN_OFFSET     = 63;
    private const int EXPONENT_OFFSET = 52;

    // [FieldOffset] attribute is .NET's way of defining how to explicitly
    // layout the fields of a structure - we're using it to overlay the
    // double and long into a single bit-space ... effectively a C# 'union'
    [FieldOffset( 0 )] private double DoubleValue;
    [FieldOffset( 0 )] private ulong LongValue;

    public IEEE754(double val)
    {
        DoubleValue = val;
    }
    // properties that retrieve the various pieces of an IEEE754 double
    public long Fraction { get { return (long)(LongValue & FRACTION_BITS); } }
    public long Exponent { get { return (long)((LongValue & EXPONENT_BITS) >> EXPONENT_OFFSET); } }
    public long Sign     { get { return (long)((LongValue & SIGN_BITS) >> SIGN_OFFSET); } }

    public void Set( double val ) { DoubleValue = val; }
}

public static void TestFunction()
{
    var array = Enumerable.Range( 1, 10000 ).ToArray();   // test array...

    // however you access your random generator would go here...
    var rand = new YourRandomNumberGenerator();

    // crack the double using the special union structure we created...
    var dul = new IEEE754( rand.GenRandomNumber() );

    // use the factional value modulo the array length as a random index...
    var randomValue = array[dul.Fraction % array.Length];
}

#4


Have you considered using the .NET Random class?

您是否考虑过使用.NET Random类?

#5


I would use Random.Next(Int32), which returns a value less than the input and >= zero. Pass your array length as the input, and you've got a random valid index.

我会使用Random.Next(Int32),它返回一个小于输入的值并且> =零。将您的数组长度作为输入传递,并且您有一个随机有效索引。

#6


As other people have already noted, System.Random has a Next overload that will do what you're asking already.

正如其他人已经注意到的那样,System.Random有一个Next重载,可以完成你已经提出的问题。

As to your comment about Convert.ToInt32 and a more efficient alternative, you can directly cast a double to an int:

至于你对Convert.ToInt32的评论和更有效的替代方案,你可以直接将一个double转换为int:

double d = 1.5;
int i = (int)d;

#7


private static readonly Random _random = new Random();

public double GetRandomVal()
{
    int z = _random.Next(vals.Length);
    return vals[z];
}

#8


If you are not constraint to use your random function, use the Random class.

如果您不限制使用随机函数,请使用Random类。

public Double GetRandomValue(Double[] values)
{
    return values[new Random().Next(values.Length)];
}

Else I would just use a cast because it gives the right behavior - rounding towards zero instead of the closest integer as Convert.ToInt32() does.

否则我会使用强制转换,因为它给出了正确的行为 - 舍入为零而不是像Convert.ToInt32()那样的最接近的整数。

public Double GetRandomValue(Double[] values)
{
    return values[(Int32)(GetNextRandomNumber() * values.Length)];
}

#1


Try using a random integer instead:

请尝试使用随机整数:

Random random = new Random();
int randomNumber = random.Next(0, vals.Length);
return vals[randomNumber];

#2


return vals[rng.Next(vals.Length)];

Where rng is

rng是哪里

Random rng = new Random();

#3


I think you have the simplest most direct implementation already identified.

我认为你已经确定了最简单,最直接的实现。

But if you are looking for performance gains in your random indexing algorithm, you may be able to just 'crack' the IEEE 754 encoded double into its exponent and fraction - and use the fraction modulo the array size as a random index.

但是,如果您正在寻找随机索引算法中的性能提升,您可以将IEEE 754编码的双重“破解”为其指数和分数 - 并使用模数作为随机索引模数。

This technique IS NOT likely to be cryptographically secure - so if that is a consideration - don't do it.

这种技术不太可能是加密安全的 - 所以如果这是一个考虑因素 - 不要这样做。

Also, this approach DOES NOT make the code more obvious - I would stick with your original implementation unless maximizing performance is the consideration. As an aside, the slowest part of this processing is most likely the Mersenne Twister generation of random numbers.

此外,这种方法不会使代码更明显 - 我会坚持你的原始实现,除非考虑最大化性能。顺便说一下,这个处理过程中最慢的部分很可能是Mersenne Twister随机数。

Here's the code:

这是代码:

[StructLayout(LayoutKind.Explicit)] // used create a union of Long and Double
public struct IEEE754
{
    private const ulong SIGN_BITS     = 0x8000000000000000;
    private const ulong EXPONENT_BITS = 0x7FF0000000000000;
    private const ulong FRACTION_BITS = 0x000FFFFFFFFFFFFF;

    private const int SIGN_OFFSET     = 63;
    private const int EXPONENT_OFFSET = 52;

    // [FieldOffset] attribute is .NET's way of defining how to explicitly
    // layout the fields of a structure - we're using it to overlay the
    // double and long into a single bit-space ... effectively a C# 'union'
    [FieldOffset( 0 )] private double DoubleValue;
    [FieldOffset( 0 )] private ulong LongValue;

    public IEEE754(double val)
    {
        DoubleValue = val;
    }
    // properties that retrieve the various pieces of an IEEE754 double
    public long Fraction { get { return (long)(LongValue & FRACTION_BITS); } }
    public long Exponent { get { return (long)((LongValue & EXPONENT_BITS) >> EXPONENT_OFFSET); } }
    public long Sign     { get { return (long)((LongValue & SIGN_BITS) >> SIGN_OFFSET); } }

    public void Set( double val ) { DoubleValue = val; }
}

public static void TestFunction()
{
    var array = Enumerable.Range( 1, 10000 ).ToArray();   // test array...

    // however you access your random generator would go here...
    var rand = new YourRandomNumberGenerator();

    // crack the double using the special union structure we created...
    var dul = new IEEE754( rand.GenRandomNumber() );

    // use the factional value modulo the array length as a random index...
    var randomValue = array[dul.Fraction % array.Length];
}

#4


Have you considered using the .NET Random class?

您是否考虑过使用.NET Random类?

#5


I would use Random.Next(Int32), which returns a value less than the input and >= zero. Pass your array length as the input, and you've got a random valid index.

我会使用Random.Next(Int32),它返回一个小于输入的值并且> =零。将您的数组长度作为输入传递,并且您有一个随机有效索引。

#6


As other people have already noted, System.Random has a Next overload that will do what you're asking already.

正如其他人已经注意到的那样,System.Random有一个Next重载,可以完成你已经提出的问题。

As to your comment about Convert.ToInt32 and a more efficient alternative, you can directly cast a double to an int:

至于你对Convert.ToInt32的评论和更有效的替代方案,你可以直接将一个double转换为int:

double d = 1.5;
int i = (int)d;

#7


private static readonly Random _random = new Random();

public double GetRandomVal()
{
    int z = _random.Next(vals.Length);
    return vals[z];
}

#8


If you are not constraint to use your random function, use the Random class.

如果您不限制使用随机函数,请使用Random类。

public Double GetRandomValue(Double[] values)
{
    return values[new Random().Next(values.Length)];
}

Else I would just use a cast because it gives the right behavior - rounding towards zero instead of the closest integer as Convert.ToInt32() does.

否则我会使用强制转换,因为它给出了正确的行为 - 舍入为零而不是像Convert.ToInt32()那样的最接近的整数。

public Double GetRandomValue(Double[] values)
{
    return values[(Int32)(GetNextRandomNumber() * values.Length)];
}