Гауссово случайное распределение в Postgresql - PullRequest
6 голосов
/ 24 февраля 2012

У меня есть таблица, скажем, 250 URL:

create table url (
  id serial,
  url varchar(64)
)

Эти URL соответствуют каждому веб-сайту.Каждый из сайтов имеет разную популярность.Допустим, что id=125 (тот, который сосредоточен на гауссиане) является самым популярным, id=1 или id=250 - наименее популярным.

Я хочу заполнить таблицу "log"как и следующий, со значением url среди тех, что указаны в таблице «url», но с учетом того, что различные URL-адреса могут появляться чаще (например, наиболее популярным будет URL-адрес с идентификатором 125).

create table log (
  id serial,
  url_id integer
)

Я хочу избегать использования random(), поскольку оно однородно и не очень "реально".

Как этого можно добиться с помощью Postgresql?

Ответы [ 4 ]

9 голосов
/ 04 июля 2013

Я искал способ генерирования чисел в соответствии с гауссовским распределением и впервые нашел этот пост.Вот почему я делюсь тем, что нашел сразу после:

Существует, по крайней мере, PostGreSQL 8.4, дополнительный модуль с именем tablefunc (http://www.postgresql.org/docs/9.2/static/tablefunc.html).

Предлагает функцию normal_rand (n(например, stddev) генерирует n псевдослучайных чисел с использованием гауссовского распределения (поэтому эта функция возвращает набор значений, обычно используемых в предложении FROM). Однако, если вы установите n равным 1, его можно использовать как функциювозвращая значение, а не набор значений.

Учитывая таблицу nb10, содержащую 10 записей, два следующих запроса возвращают набор из 10 псевдослучайных чисел после стандартного распределения Гаусса (среднее = 0, stddev = 1))

SELECT normal_rand(1, 0, 1) FROM nb10;

и

SELECT * from normal_rand(10, 0, 1);

Я надеюсь, что это может помочь кому-нибудь в будущем ...: -)

Чтобы ответить на ваш вопрос конкретно, вы можете использоватьчто-то вроде:

SELECT floor(random_rand(1, 0, 1) * 250 + 125);

К сожалению, с помощью этого запроса можно получить ответ не в диапазоне [0, 249].Например, вы могли бы:

  • использовать рекурсивный запрос, который я считаю немного излишним, для отбрасывания значений, не входящих в диапазон [0, 249], или
  • сделать ваш выбор вцикл на ваш основной язык, принимая значение только в том случае, если оно находится в диапазоне [0, 249] или
  • , используя оператор по модулю, чтобы остаться в диапазоне [0, 250 [, я думаю, этоявляется лучшим решением, хотя оно слегка изменяет гауссову кривую.Вот последний запрос, который я предлагаю вам использовать (трюки по модулю / + / modulo заключаются в том, что -x по модулю y с положительным числом x дает отрицательное число в PostGreSQL, что неплохо: p):

    SELECT ((floor(normal_rand(1,0,1)*250 + 125)::int % 250) + 250) % 250 as v;
    
7 голосов
/ 18 января 2014

Сумма из 12 равномерных распределений в диапазоне [0, 1) является хорошим приближением к гауссову распределению, ограниченному в диапазоне [0, 12). Затем его можно легко перемасштабировать, умножив на константу, а затем добавив / вычтя константу.

select
    random() + 
    random() + 
    random() +
    random() + 
    random() + 
    random() +
    random() + 
    random() + 
    random() +
    random() + 
    random() + 
    random();

http://books.google.com/books?id=EKA-yeX2GVgC&pg=PA185&lpg=PA185&dq=%22sum+of+12+uniform+random+variables%22&source=bl&ots=YfwwE0fBB3&sig=HX9J9Oe6x316kVL8uamDU_GOsn4&hl=en&sa=X&ei=bJLZUur1GozaqwGHm4DQDQ&ved=0CEUQ6AEwAw#v=onepage&q=%22sum%20of%2012%20uniform%20random%20variables%22&f=false

1 голос
/ 21 ноября 2017

Модуль tablefunc предоставляет случайную функцию с нормальным распределением.Вы можете проверить, установлен ли он, используя:

SELECT normal_rand(1, 0, 1); -- generates 1 single value with mean 0 and a standard deviation of 1

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

Если он не установлен, попробуйте следующее:

CREATE EXTENSION "tablefunc";

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

1 голос
/ 27 марта 2013

Простой факт заключается в том, что вы хотите создать свою собственную функцию, которая упаковывает rand () во что-то, что обеспечивает гауссово распределение неявно или явно.

У меня нет статистических данных, чтобы рассказать вам, как преобразовать равномерное распределение в гауссовское, но вам придется написать конвертер. Что-то вроде того, что указано в http://www.perlmonks.org/?node_id=26889 (если вам не нравится Perl, вы можете переписать это в pl / pgsql или даже в простом SQL).

CREATE OR REPLACE FUNCTION gaussian_rand() RETURNS numeric LANGUAGE PLPERL VOLATILE AS
$$
    my ($u1, $u2);  # uniformly distributed random numbers
    my $w;          # variance, then a weight
    my ($g1, $g2);  # gaussian-distributed numbers

    do {
        $u1 = 2 * rand() - 1;
        $u2 = 2 * rand() - 1;
        $w = $u1*$u1 + $u2*$u2;
    } while ( $w >= 1 );

    $w = sqrt( (-2 * log($w))  / $w );
    $g2 = $u1 * $w;
    $g1 = $u2 * $w;
    # return both if wanted, else just one
    return $g1;

$$;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...