【问题标题】:Gaussian random distribution in PostgresqlPostgresql中的高斯随机分布
【发布时间】:2012-03-14 23:19:29
【问题描述】:

我有一个假设为 250 个 URL 的表格:

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

这些 URL 对应一个网站。每个网站都有不同的受欢迎程度。假设id=125(以高斯为中心的那个)最受欢迎,id=1id=250 最不受欢迎。

我想在“url”表中提供的值中填充一个类似于下面的“日志”表,但考虑到不同的 URL 可能会更频繁地出现(例如 url,其 id 为125 将是最受欢迎的)。

create table log (
  id serial,
  url_id integer
)

我想避免使用random(),因为它是统一的,不是很“真实”。

如何使用 Postgresql 实现这一点?

【问题讨论】:

  • 为什么你认为流行度或排名具有高斯分布?
  • 您可以使用 RAND 使用该分布的 PDF 计算任何分布(产生 0 和 1 之间的值,对吗?)。对于高斯发行版,这将是 1/2(1 + erf(x-mu)/sqrt(2sigma^2)) - 请参阅 en.wikipedia.org/wiki/Normal_distribution
  • @wildplasser :因为这条定律对于我尝试建模的东西来说似乎相当不错。我承认它可能是任何其他的!

标签: postgresql random statistics gaussian


【解决方案1】:

范围 [0, 1) 上的 12 个均匀分布的总和很好地近似于范围 [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

【讨论】:

  • 我接受了这个,因为我发现它是最简单和最优雅的方式,无论使用什么语言。感谢所有其他贡献者。
【解决方案2】:

我正在寻找一种根据高斯分布生成数字的方法,并首先找到了这篇文章。这就是为什么我分享我刚刚发现的内容:

至少从 PostgreSQL 8.4 开始,还有一个名为 tablefunc (http://www.postgresql.org/docs/current/static/tablefunc.html) 的附加模块。

它提出了一个函数 normal_rand(n, mean, stddev) 使用高斯分布生成 n 个伪随机数(因此该函数返回一组值,通常在 FROM 子句中使用)。但是,如果将 n 设置为 1,则它可以用作返回值而不是一组值的函数。

考虑包含 10 条记录的表 nb10,以下两个查询返回一组 10 个遵循标准高斯分布的伪随机数(均值 = 0,标准差 = 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[ 范围内,我认为这是最好的解决方案,尽管它会稍微改变高斯曲线。这是我建议您使用的最后一个查询(取模/+/取模技巧是因为 -x 取模 y 与 x 为正数会在 PostgreSQL 中给出一个负数,这不是一件坏事:p):

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

【讨论】:

    【解决方案3】:

    简单的事实是您想创建自己的函数,该函数将 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;
    
    $$;
    

    【讨论】:

      【解决方案4】:

      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 登录。

      【讨论】:

      • 哦,这也非常有趣,例如现在打开了透视表的视野。非常感谢!
      【解决方案5】:

      也可以直接在内置语言PL/PgSQL中实现

      create or replace
      function random_gauss( avg real = 0, stddev real = 1 )
      returns real language plpgsql as $$
      declare x1 real; x2 real; w real;
      begin
        loop
          x1 = 2.0 * random() - 1.0;
          x2 = 2.0 * random() - 1.0;
          w = x1*x1 + x2*x2;
          exit when w < 1.0;
        end loop;
        return avg + x1 * sqrt(-2.0*ln(w)/w) * stddev;
      end; $$;
      
      with data as (
        select t, random_gauss(100,15)::integer score from generate_series(1,1000000) t
      )
      select
        score,
        sum(1),
        repeat('=',sum(1)::integer/500) bar
      from data
      where score between 60 and 140
      group by score
      order by 1;
      
      rollback;
      

      这从 100 万个数字的样本中得到了类似的结果,平均为 100,标准差为 15。

       score |  sum  |                          bar
      -------+-------+--------------------------------------------------------
          60 |   764 | =
          61 |   893 | =
          62 |  1059 | ==
          63 |  1269 | ==
          64 |  1524 | ===
          65 |  1740 | ===
          66 |  1990 | ===
          67 |  2346 | ====
          68 |  2741 | =====
          69 |  3160 | ======
          70 |  3546 | =======
          71 |  4109 | ========
          72 |  4633 | =========
          73 |  5252 | ==========
          74 |  5952 | ===========
          75 |  6536 | =============
          76 |  7429 | ==============
          77 |  8140 | ================
          78 |  9061 | ==================
          79 | 10063 | ====================
          80 | 10844 | =====================
          81 | 11911 | =======================
          82 | 13180 | ==========================
          83 | 13880 | ===========================
          84 | 15111 | ==============================
          85 | 16016 | ================================
          86 | 17310 | ==================================
          87 | 18262 | ====================================
          88 | 19615 | =======================================
          89 | 20400 | ========================================
          90 | 21186 | ==========================================
          91 | 22190 | ============================================
          92 | 23103 | ==============================================
          93 | 24150 | ================================================
          94 | 24327 | ================================================
          95 | 24992 | =================================================
          96 | 25505 | ===================================================
          97 | 25868 | ===================================================
          98 | 26146 | ====================================================
          99 | 26574 | =====================================================
         100 | 27104 | ======================================================
         101 | 26599 | =====================================================
         102 | 26345 | ====================================================
         103 | 25940 | ===================================================
         104 | 25485 | ==================================================
         105 | 25157 | ==================================================
         106 | 24827 | =================================================
         107 | 23844 | ===============================================
         108 | 23262 | ==============================================
         109 | 22211 | ============================================
         110 | 21326 | ==========================================
         111 | 20315 | ========================================
         112 | 19496 | ======================================
         113 | 18026 | ====================================
         114 | 17182 | ==================================
         115 | 16026 | ================================
         116 | 14979 | =============================
         117 | 13959 | ===========================
         118 | 12840 | =========================
         119 | 11718 | =======================
         120 | 11169 | ======================
         121 | 10037 | ====================
         122 |  9273 | ==================
         123 |  8041 | ================
         124 |  7402 | ==============
         125 |  6761 | =============
         126 |  5827 | ===========
         127 |  5257 | ==========
         128 |  4736 | =========
         129 |  4153 | ========
         130 |  3494 | ======
         131 |  3103 | ======
         132 |  2731 | =====
         133 |  2379 | ====
         134 |  2064 | ====
         135 |  1696 | ===
         136 |  1481 | ==
         137 |  1246 | ==
         138 |  1024 | ==
         139 |   910 | =
         140 |   788 | =
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-09
        • 2015-04-22
        • 2020-08-24
        • 2015-09-20
        • 2012-10-08
        • 1970-01-01
        • 2011-06-12
        • 1970-01-01
        相关资源
        最近更新 更多