c # Самый быстрый способ случайного индексирования в массив - PullRequest
2 голосов
/ 20 июля 2009

У меня есть массив двойных значений "vals", мне нужно случайным образом проиндексировать этот массив и получить значение. GenRandomNumber () возвращает число от 0 до 1, но никогда не 0 или 1. Я использую Convert.ToInt32, чтобы в основном получить все слева от моего десятичного знака, но должен быть более эффективный способ сделать это?

Вот мой код:

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

Спасибо

Обновление

Спасибо всем, кто ответил, но я вынужден использовать предоставленную реализацию MersenneTwister для случайных чисел, которая имеет метод rand.NextDouble ()

Обновление 2

Подумав еще об этом, все, что мне нужно сделать, это генерировать случайное число от 0 до array.length-1, а затем использовать его для случайной индексации в массиве . длина vals составляет 2 ^ 20 = 1048576, поэтому генерации случайного числа int достаточно. Я заметил, что у моего MersenneTwister есть метод:

public int Next(int maxValue)

Если я назову это как vals [rand.Next (vals.length-1)] что должно сделать это правильно? Я также вижу, что у MersenneTwister есть конструктор:

public MersenneTwister(int[] init)

Не уверен, для чего это нужно, могу ли я использовать это для предварительного заполнения допустимых случайных чисел, для которых я предоставляю массив от 0 до vals.length?

FYI vals - это двойной массив длиной 1048576, разделяющий кривую нормального распределения. Я в основном использую этот механизм для создания нормально распределенных чисел как можно быстрее, в симуляции Монте-Карло используются миллиарды нормально распределенных случайных чисел каждый день , так что каждый маленький кусочек помогает.

Ответы [ 8 ]

9 голосов
/ 20 июля 2009

Попробуйте использовать случайное целое число вместо:

Random random = new Random();
int randomNumber = random.Next(0, vals.Length);
return vals[randomNumber];
3 голосов
/ 20 июля 2009
return vals[rng.Next(vals.Length)];

Где рнг

Random rng = new Random();
2 голосов
/ 20 июля 2009

Я думаю, у вас есть самая простая и прямая реализация, уже определенная.

Но если вы ищете прирост производительности в своем алгоритме случайного индексирования, вы можете просто '1003 * взломать ' IEEE 754, закодированный в два раза в его экспоненту и дробь - и использовать дробь по модулю массива размер как случайный индекс.

Этот метод вряд ли будет криптографически безопасным, поэтому, если это важно, не делайте этого.

Кроме того, этот подход НЕ делает код более наглядным - я бы придерживался вашей исходной реализации, если только соображение максимизирует производительность. Кроме того, самой медленной частью этой обработки, скорее всего, является случайное число Мерсенна Твистера.

Вот код:

[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];
}
0 голосов
/ 20 июля 2009

Как уже отмечали другие люди, System.Random имеет перегрузку Next, которая будет делать то, что вы уже просили.

Что касается вашего комментария о Convert.ToInt32 и более эффективной альтернативе, вы можете напрямую привести double к int:

double d = 1.5;
int i = (int)d;
0 голосов
/ 20 июля 2009

Если вы не ограничены в использовании случайной функции, используйте класс Random.

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

Иначе, я бы просто использовал приведение, потому что оно дает правильное поведение - округление до нуля вместо ближайшего целого числа, как Convert.ToInt32().

public Double GetRandomValue(Double[] values)
{
    return values[(Int32)(GetNextRandomNumber() * values.Length)];
}
0 голосов
/ 20 июля 2009

Я бы использовал Random.Next (Int32) , который возвращает значение меньше входного и> = ноль. Передайте длину массива в качестве входных данных, и вы получите случайный действительный индекс.

0 голосов
/ 20 июля 2009

Рассматривали ли вы использование .NET Random класса?

0 голосов
/ 20 июля 2009
private static readonly Random _random = new Random();

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