2012-02-24 2 views
5

У меня есть таблица, скажем 250 URL-адресов:Gaussian случайное распределение в Postgresql

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

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

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

create table log (
    id serial, 
    url_id integer 
) 

Я хочу, чтобы избежать использования random(), поскольку она является однородной и не очень «реальной».

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

+2

Почему вы предполагаете, что популярность или рейтинг имеют распределение Гауссиона? – wildplasser

+2

Вы можете рассчитать любое распределение, используя PDF этого дистрибутива, используя RAND (который производит значения от 0 до 1, справа?). Для гауссового дистрибутива это будет 1/2 (1 + erf (x-mu)/sqrt (2sigma^2)) - см. Http://en.wikipedia.org/wiki/Normal_distribution –

+0

@wildplasser: потому что этот закон кажется довольно хорошо для того, что я пытаюсь моделировать. Я признаю, что это могло быть и другое! – SCO

ответ

6

Сумма 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

+0

Я принял это, потому что нашел его самым простым и элегантным способом, независимо от того, какой язык используется. Спасибо всем другим участникам. – SCO

1

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

У меня нет статистического фона, чтобы рассказать вам, как преобразовать равномерное распределение в гауссовский, но вам нужно написать конвертер. Что-то вроде приведенного в 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; 

$$; 
7

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

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

Предлагает функцию normal_rand (n, mean, 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] или
  • использовать оператор modulo, чтобы оставаться в [0, 250 [диапазон, я думаю, это лучшее решение, хотя оно немного изменяет гауссовский кривая. Вот окончательный запрос я предлагаю вам использовать (по модулю/+/по модулю фокусы, потому что -й по модулю у с ха положительным числом дает отрицательное число в PostGreSQL, что не плохо: р):

    SELECT ((floor(normal_rand(1,0,1)*250 + 125)::int % 250) + 250) % 250 as v; 
    
1

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

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

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

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

CREATE EXTENSION "tablefunc"; 

В противном случае вам нужно будет войти в систему как a super user and install the module.

+0

О, это тоже очень интересно, и теперь открывается горизонты для поворота таблиц. Большое спасибо ! – SCO

Смежные вопросы