Генерируйте случайное двоичное число с переменной пропорцией '1' биты

Мне нужна функция для генерации случайных целых чисел. (примите Java long введите на данный момент, но это будет расширено на BigInteger или BitSet позже.)

Хитрая часть существует параметр P, который указывает (независимую) вероятность любого бита в результате, являющемся 1.

Если P = 0.5 затем мы можем просто использовать стандартный генератор случайных чисел. Некоторые другие значения P также легко реализовать. Вот неполный пример:

Random random = new Random();

// ...

long nextLong(float p) {
    if      (p == 0.0f)   return 0L;
    else if (p == 1.0f)   return -1L;
    else if (p == 0.5f)   return random.nextLong();
    else if (p == 0.25f)  return nextLong(0.5f) & nextLong(0.5f);
    else if (p == 0.75f)  return nextLong(0.5f) | nextLong(0.5f);
    else if (p == 0.375f) return nextLong(0.5f) & nextLong(0.75f); // etc
    else {
      // What goes here??
      String message = String.format("P=%f not implemented yet!", p);
      throw new IllegalArgumentException(message);
    }
}

Существует ли способ обобщить это для какого-либо значения P между 0,0 и 1.0?

6
задан Charles 19 March 2012 в 19:25
поделиться

6 ответов

Вот как я решил его в конце.

  1. Создайте целое число n между 0..16, следуя биномиальному распределению. Это дает количество «1» битов в 16-битном частичном результате.
  2. Случайно генерируют индекс в таблицу поиска, которая содержит 16-битные целые числа, содержащие желаемое число битов «1».
  3. Повторите 4 раза, чтобы получить четыре 16-битных целых числа.
  4. Сращивание этих четырех 16-битных целых чисел вместе, чтобы получить 64-битное целое число.

Это было частично вдохновлено ответ Ondra žižka.

Преимущество заключается в том, что он уменьшает количество вызовов на Random.nextlong () до 8 вызовов на 64 битах выхода. Для сравнения прокат для каждого отдельного бита потребует 64 вызовов. Побитовые и / или используемые от 2 до 32 вызовов в зависимости от значения P

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

Это много кода, но он выплачивает с точки зрения производительности.


Обновление - объединил это с побитовым и / или решением. Он теперь использует этот метод, если он догадается, это будет более эффективным (с точки зрения вызовов на Random.Next () .).)

2
ответ дан 9 December 2019 в 22:35
поделиться

Сначала немного уродливая математика, которую вы уже используете в своем коде.

Определить X и Y - биты с вероятностью того, чтобы быть 1 из x = p (x = 1), y = p (y = 1) соответственно. Тогда у нас есть это

 p( x & y = 1) = X Y
 p( x | y = 1) = 1 - (1-X) (1-Y)
 p( x ^ y = 1) = X (1 - Y) + Y (1 - X)

сейчас, если мы выпустим y = 1/2, мы получаем

P( x & y ) = X/2
P( x | y ) = (X+1)/2

теперь установите RHS к вероятности, которую мы хотим, и у нас есть два случая, которые мы можем решить для X

X = 2 p        // if we use &
X = 2 p - 1    // if we use |

, мы предполагаем, что мы можем использовать Это опять же, чтобы получить X с точки зрения другой переменной Z ... И тогда мы продолжаем итерацию, пока мы не сделаем «достаточно».

Это немного неясно, но рассмотрим P = 0,375

0.375 * 2 = 0.75  < 1.0 so our first operation is &
0.75 * 2 = 1.5 > 1.0 so our second operation is |
0.5 is something we know so we stop.

, таким образом, мы можем получить переменную с p = 0,375 на x1 & (x2 | x3)

. Проблема в том, что для большинства переменных это не будет расторгнуть. например

0.333 *2 = 0.666 < 1.0 so our first operation is &
0.666 *2 = 1.333 > 1.0 so our second operation is |
0.333 *2 = 0.666 < 1.0 so our third operation is &
etc...

Так что p = 0,333 может быть создан

X1 & ( X2 | (X3 & (X4 | ( ... ) ) ) )

, теперь я подозреваю, что достаточное количество условий в серии даст вам достаточно точности, и это может быть написано как рекурсивная функция. Однако может быть лучший способ, которым это тоже ... Я думаю, что порядок операций связан с двоичным представлением P, я просто не уверен, что именно то, как ... и не успевать думать об этом глубже.

Во всяком случае, вот какой-то непроверенный код C ++, который делает это. Вы должны быть в состоянии удалить его легко.

uint bitsWithProbability( float p )
{
   return bitsWithProbabilityHelper( p, 0.001, 0, 10 );
}

uint bitsWithProbabilityHelper( float p, float tol, int cur_depth, int max_depth )
{
   uint X = randbits();
   if( cur_depth >= max_depth) return X;
   if( p<0.5-tol)
   {
     return X & bitsWithProbabilityHelper( 2*p, 0.001, cur_depth+1, max_depth );
   }
   if(p>0.5+tol)
   {
     return X | bitsWithProbabilityHelper( 2*p-1, 0.001, cur_depth+1, max_depth );
   }
   return X;
}
4
ответ дан 9 December 2019 в 22:35
поделиться

Распространение пропорционального количества битов на ходу номер. Псевдокод:

long generateNumber( double probability ){
  int bitCount = 64 * probability;
  byte[] data = new byte[64]; // 0-filled

  long indexes = getRandomLong();

  for 0 to bitCount-1 {
    do { 
      // distribute this bit to some postition with 0.
      int index = indexes & 64;
      indexes >> 6;
      if( indexes == 0 ) indexes = getRandomLong();
    } while ( data[index] == 0 );
    data[index] = 1;
  }

  return bytesToLong( data );
}    

Я надеюсь, что вы получите то, что я имею в виду. Возможно, байт [] может быть заменен длинным длинным и битными операциями, чтобы сделать его быстрее.

2
ответ дан 9 December 2019 в 22:35
поделиться

Просто «SVN копируйте» отсутствующие папки из ветви до багажника рекурсивно.

-121--3021061-

Используйте случайный генератор, который генерирует равномерный номер поплавка от 0 до 1. Если R> P, затем установить бит до 0, в противном случае установите его на 1

1
ответ дан 9 December 2019 в 22:35
поделиться

Если вы хотите применить некоторое распространение, где с вероятностью P Вы получаете 1 и с вероятностью 1-р. Вы получаете 0 в любой конкретный бит, ваша лучшая ставка - просто генерировать каждый бит независимо с вероятностью P быть в 1 (это звучит как рекурсивное определение, я знаю).

Вот решение, я прогуляю через него ниже:

public class MyRandomBitGenerator
{

    Random pgen = new Random();

    // assumed p is well conditioned (0 < p < 1)
    public boolean nextBitIsOne(double p){
        return pgen.nextDouble() < p ? true : false;
    }

    // assumed p is well conditioned (0 < p < 1)
    public long nextLong(double p){
        long nxt = 0;
        for(int i = 0; i < 64; i++){
           if(nextBitIsOne(p)){
               nxt += 1 << i;
           }
        }
        return nxt;
    }

}

в основном мы сначала определим, как генерировать значение 1 с вероятностью p: PGEN.NextDouble () генерирует число между 0 И 1 с равномерной вероятностью, задавая, если это меньше P , мы отбираемся, что мы отбираемся, что мы ожидаем увидеть p 1s, когда мы вызываем эту функцию бесконечно.

1
ответ дан 9 December 2019 в 22:35
поделиться

Обратная совместимость на каждой платформе, за исключением того, чья ABI была выбрана.

-121--1403582-

JColliDer - это интерфейс Java к серверу синтеза Supercollider. Если вы хотите синтезировать музыку, это сделает вещи намного проще (оно тезисы от генератора тона к синтезатору, позаботится о таких вещах, как генерация графа, удаляя приглушенные синтезаторы из графика синтеза, пока они не понадобится, исправляют сигналы между синтезацией динамически и т. Д.).

-121--2296635-

Вот еще один вариант Ответ Михаила Андерсона

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

class BitsWithProbabilityHelper {
    public BitsWithProbabilityHelper(float prob, Random rnd) {
        if (Float.isNaN(prob)) throw new IllegalArgumentException();

        this.rnd = rnd;

        if (prob <= 0f) {
            zero = true;
            return;
        }

        // Decode IEEE float
        int probBits = Float.floatToIntBits(prob);
        mantissa = probBits & 0x7FFFFF;
        exponent = probBits >>> 23;

        // Restore the implicit leading 1 (except for denormals)
        if (exponent > 0) mantissa |= 0x800000;
        exponent -= 150;

        // Force mantissa to be odd
        int ntz = Integer.numberOfTrailingZeros(mantissa);
        mantissa >>= ntz;
        exponent += ntz;
    }

    /** Determine how many random words we need from the system RNG to
     *  generate one output word with probability P.
     **/
    public int iterationCount() {
        return - exponent;
    }

    /** Generate a random number with the desired probability */
    public long nextLong() {
        if (zero) return 0L;

        long acc = -1L;
        int shiftReg = mantissa - 1;
        for (int bit = exponent; bit < 0; ++ bit) {
            if ((shiftReg & 1) == 0) {
                acc &= rnd.nextLong();
            } else {
                acc |= rnd.nextLong();
            }
            shiftReg >>= 1;
        }
        return acc;
    }

    /** Value of <code>prob</code>, represented as m * 2**e where m is always odd. */
    private int exponent;  
    private int mantissa;

    /** Random data source */
    private final Random rnd;

    /** Zero flag (special case) */
    private boolean zero;
}
1
ответ дан 9 December 2019 в 22:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: