2016-10-01 3 views
3

Мне было интересно, есть ли способ иметь случайное число между A a b и где, если число удовлетворяет определенному требованию, оно скорее появится, чем все остальные числа между A и B, например: более низкие числа, скорее всего, появятся, так что если A = 1 и B = 10, то 1 будет наиболее вероятным, а 10 - самым незаменимым.Случайные ints с разными вероятностями

Вся помощь ценится :) (простите за плохой английский/грамматика/вопрос)

+0

Вы ищете распределение zipf? http://stackoverflow.com/questions/9983239/how-to-generate-zipf-distributed-numbers-efficiently – HazemGomaa

ответ

2

C++ 11 (который вы абсолютно должны использовать к настоящему времени) добавили заголовок <random> к стандарту C++ библиотека. Этот заголовок обеспечивает гораздо более качественные генераторы случайных чисел для C++. Использование srand() и rand() никогда не было очень хорошей идеей, потому что нет гарантии качества, но теперь это действительно непростительно.

В вашем примере звучит так, как будто вы хотите, что можно было бы назвать «дискретным треугольным распределением»: функция массы вероятности выглядит как треугольник. Самый простой (но, возможно, не самый эффективный) способ осуществить это в C++ будет дискретное распределение включенных в <random>:

auto discrete_triangular_distribution(int max) { 
    std::vector<int> weights(max); 
    std::iota(weights.begin(), weights.end(), 0); 
    std::discrete_distribution<> dist(weights.begin(), weights.end()); 
    return dist; 
} 

int main() { 
    std::random_device rd; 
    std::mt19937 gen(rd()); 
    auto&& dist = discrete_triangular_distribution(10); 
    std::map<int, int> counts; 
    for (int i = 0; i < 10000; i++) 
     ++counts[dist(gen)]; 
    for (auto count: counts) 
     std::cout << count.first << " generated "; 
     std::cout << count.second << " times.\n"; 
} 

которая для меня дает следующий результат:

1 generated 233 times. 
2 generated 425 times. 
3 generated 677 times. 
4 generated 854 times. 
5 generated 1130 times. 
6 generated 1334 times. 
7 generated 1565 times. 
8 generated 1804 times. 
9 generated 1978 times. 

Things более сложной чем это было бы лучше обслуживать либо с использованием одного из существующих дистрибутивов (мне сказали, что все широко используемые статистические распределения включены), либо написав собственный дистрибутив, что не слишком сложно: он просто должен быть объектом с оператор вызова функции, который принимает генератор случайных бит и использует эти биты для создания (в этот случай) случайные числа. Но вы можете создать тот, который сделал случайные строки или любые произвольные случайные объекты, возможно, для целей тестирования).

+0

Из вопроса, похоже, что выходы должны быть в диапазоне * [1, 10] *, и что более низкие цифры должны быть * более вероятными. –

+0

Это просто пример. –

+0

Большое спасибо за вашу помощь! Я просто изменил: ++ counts [dist (gen)]; to ++ counts [abs (10 - dist (gen))]; чтобы сделать более низкие цифры более вероятными :) – AdminBenni

-1

Вы можете создать массив значений, тем более вероятно, значение имеет несколько индексов, а затем выбрать случайный индекс.

пример:

int random[55]; 
int result; 

    int index = 0; 
    for (int i = 1 ; i <= 10 ; ++i) 
     for (int j = i ; j <= 10 ; ++j) 
      random[index++] = i; 

result = random[rand() % 55]; 

Кроме того, вы можете попытаться получить случайное число в два раза, первый раз, когда вы выбираете максимальное количество, то вы выбираете случайное число:

int max= rand() % 10 + 1; // This is your max value 
int random = rand() % max + 1; // This is you result 

Оба пути составит 1 более вероятно, чем на 2, 2 более вероятно, чем на 3 ... 9, более вероятно, чем на 10.

+1

Да, это так. Каждый вопрос требует правильного решения. –

2

В вашем вопросе не указывается, какой дистрибутив использовать. Одним из вариантов (многих) является использование (negative) exponential distribution. Это распределение параметризуется параметром & lambda;. Для каждого значения & lambda;, максимальный результат является неограниченным (который должен быть обработан, чтобы вернуть результаты только в диапазоне, указанном)

enter image description here

(из Википедии, По Skbkekas, CC BY 3.0)

так что любой & lambda; теоретически может работать; Однако свойства ВПР

enter image description here

(из Википедии, By Skbkekas, CC BY 3.0)

следует, что он платит, чтобы выбрать что-то в порядке 1/(до - от + 1).

Следующий класс работает как стандартное распределение библиотек. Внутри он генерирует числа в цикле, пока не будет получен результат в [from, to].

#include <iostream> 
#include <iomanip> 
#include <string> 
#include <map> 
#include <random> 

class bounded_discrete_exponential_dist { 
public: 
    explicit bounded_discrete_exponential_dist(std::size_t from, std::size_t to) : 
     m_from{from}, m_to{to}, m_d{0.5/(to - from + 1)} {} 
    explicit bounded_discrete_exponential_dist(std::size_t from, std::size_t to, double factor) : 
     m_from{from}, m_to{to}, m_d{factor} {} 

    template<class Gen> 
    std::size_t operator()(Gen &gen) { 
     while(true) { 
      const auto r = m_from + static_cast<std::size_t>(m_d(gen)); 
      if(r <= m_to) 
       return r; 
     } 
    } 

private: 
    std::size_t m_from, m_to; 
    std::exponential_distribution<> m_d; 
}; 

Ниже приведен пример использования его:

int main() 
{ 
    std::random_device rd; 
    std::mt19937 gen(rd()); 

    bounded_discrete_exponential_dist d{1, 10}; 

    std::vector<std::size_t> hist(10, 0); 
    for(std::size_t i = 0; i < 99999; ++i) 
     ++hist[d(gen) - 1]; 

    for(auto h: hist) 
     std::cout << std::string(static_cast<std::size_t>(80 * h/99999.), '+') << std::endl; 
} 

При запуске он выводит гистограмму так:

$ ./a.out 
++++++++++ 
+++++++++ 
+++++++++ 
++++++++ 
+++++++ 
+++++++ 
+++++++ 
+++++++ 
++++++ 
++++++ 
+0

Действительно хороший ответ, однако я подумал, что ответ @Miles мне подходит немного лучше. – AdminBenni

+1

@AdminBenni Это прекрасно. Всего наилучшего. –

0

Ваша основная генератор случайных чисел должен производить высококачественные , равномерные случайные числа от 0 до 1 - эпсилон. Затем вы преобразовываете его, чтобы получить нужную вам дистрибуцию. Простейшим преобразованием является, конечно, (int) (p * N) в общем случае, когда требуется целое число от 0 до N -1.

Но есть много других преобразований, которые вы можете попробовать. Например, возьмите квадратный корень, чтобы смещать его на 1,0, затем 1 - p, чтобы установить смещение в сторону нуля. Или вы можете найти распределение Пуассона, которое может быть тем, чем вы являетесь. Вы также можете использовать полугауссовское распределение (статистическая колоколовая кривая с обрезанием нулевых элементов и, предположительно, также крайний хвост распределения по мере выхода за пределы диапазона).

Не может быть правильного ответа. Попробуйте различные вещи, запишите десять тысяч или около того значений и выберите тот, который дает вам нужные результаты.