代码之家  ›  专栏  ›  技术社区  ›  DanO Roman

T-SQL中的幂律分布

  •  2
  • DanO Roman  · 技术社区  · 15 年前

    我基本上需要答案 this SO question that provides a power-law distribution ,为我翻译为T-SQL。

    我想从 census provided table of names . 我想得到和人口中大致相同的分布。该表按频率排列了88799个姓名。”“史密斯”排名第一,频率为1.006%,“奥尔德林”排名88799,频率为1.7x 10^-6桑德斯“排名75,频率为0.100%。

    曲线根本不需要精确拟合。只要给我1%的“史密斯”和百万分之一的“艾德琳”

    这是我目前所拥有的。

    SELECT [LastName]
    FROM [LastNames] as LN
    WHERE LN.[Rank] = ROUND(88799 * RAND(), 0)
    

    但这当然会产生一个均匀的分布。

    我保证,当一个聪明的人做出回应时,我还是会自己想办法解决这个问题。

    4 回复  |  直到 9 年前
        1
  •  3
  •   mjv    15 年前

    既然可以从实际分配中提取,为什么还要满足于幂律分配呢?

    我建议您修改LastNames表以包含一个数字列,该列将包含一个数字值,该值表示具有更常见名称的实际indivuduals数。你可能会想要一个更小但成比例的数字,比如说,每百分之一万的表示。

    然后,列表将类似于:
    (除了问题中提到的3个名字,我猜是怀特,约翰逊等人)

    Smith          0   
    White     10,060
    Johnson   19,123
    Williams  28,456
    ...
    Sanders  200,987
    ..
    Alderink 999,997
    

    选择的名字是

    SELECT TOP 1 [LastName]
    FROM [LastNames] as LN
    WHERE LN.[number_described_above] < ROUND(100000 * RAND(), 0)
    ORDER BY [number_described_above] DESC
    

    那就是选择一个不超过[uniform distribution]随机数的名字。注意查询如何使用 小于 并下令 描述 -结束订单;这将保证第一个条目(Smith)被选中。另一种选择是,从史密斯的10060开始,而不是从零开始,并放弃小于此值的随机抽签。

    除了上面提到的边界管理问题(从零开始而不是从10060开始)之外,这个解决方案以及到目前为止的另外两个响应与 德马克 对这个问题中提到的问题的回答。基本上,我们的想法是使用CDF( 累积分布函数 ).


    编辑 :
    如果你坚持使用 数学函数而不是实际分布 ,下面应该提供幂律函数,以某种方式传达实际分布的“长尾”形状。您可能需要调整@PwrCoef值(顺便说一句,它不必是整数),实际上,系数越大,函数就越偏向列表的开头。

    DECLARE @PwrCoef INT
    SET @PwrCoef = 2
    SELECT 88799 - ROUND(POWER(POWER(88799.0, @PwrCoef) * RAND(), 1.0/@PwrCoef), 0)
    

    笔记:
    -上面函数中额外的“.0”对于强制SQL执行浮点操作而不是整数操作非常重要。
    -我们从88799中减去功率计算的原因是,计算的分布是这样的:一个数字越接近刻度的末端,就越有可能被画出来。按相反顺序排序的姓氏列表(最有可能是名字在前),我们需要这个减法。

    假设3的幂,那么查询看起来像

    SELECT [LastName]
    FROM [LastNames] as LN
    WHERE LN.[Rank]
         = 88799 - ROUND(POWER(POWER(88799.0, 3) * RAND(), 1.0/3), 0)
    

    这是问题中除最后一行之外的查询。

    重新编辑 :
    从实际分布来看,正如人口普查数据所显示的那样,曲线是非常陡峭的,需要 在上面显示的天真公式中。
    一种更明智的方法可能是分几层操作,即在累积分布的三分之三(或四分之四或……)中的每一层中执行相等数量的绘制;在这些部分列表中的每一层中,我们将使用幂律函数绘制,可能具有相同的系数,但具有不同的范围。
    例如
    假设为三分之一,则列表划分如下:

    • 前三名=425个名字,从史密斯到阿尔瓦拉多
    • 第二个三分之一=6277个名字,从到盖纳
    • 倒数第三=82097个名字,从弗里斯比到最后

    如果我们需要,比如说,1000个名字,我们会从名单的前三分之一抽取334个,从第二个三分之一抽取333个,从最后一个三分之一抽取333个。
    对于三分之一,我们会使用一个类似的公式,也许第一个三分之一的幂系数更大(我们真的有兴趣支持列表中较早的名字, 和 也就是相对频率更具统计相关性的地方)。三个选择查询可能如下所示:

    -- Random Drawing of a single Name in top third
    --   Power Coef = 12
    SELECT [LastName]
    FROM [LastNames] as LN
    WHERE LN.[Rank]
         =  425 - ROUND(POWER(POWER(425.0, 12) * RAND(), 1.0/12), 0)
    
    -- Second third; Power Coef = 7
    ...
    WHERE LN.[Rank]
         =  (425 + 6277) - ROUND(POWER(POWER(6277.0, 7) * RAND(), 1.0/7), 0)
    
    -- Bottom third; Power Coef = 4
    ...
    WHERE LN.[Rank]
         =  (425 + 6277 + 82097) - ROUND(POWER(POWER(82097.0, 4) * RAND(), 1.0/4), 0)
    
        2
  •  2
  •   Dr. belisarius    15 年前

    不要将pdf存储为rank,而是存储CDF(从Aldekirk开始,直到该名称为止的所有频率的总和)。

    然后修改您的选择以检索排名大于公式结果的第一个项次。

        3
  •  1
  •   RC_Cleland    15 年前

    我把这个问题理解为“我需要得到一系列的名字,这些名字将反映1990年美国人口普查中姓氏的频率”

    我可能读这个问题有点不同于其他的建议,虽然答案已经被接受,一个非常彻底的答案是,我将贡献我的经验与人口普查姓氏。

    我从1990年的人口普查中下载了同样的数据。我的目标是在一个病历应用程序的性能测试过程中产生大量的名字提交给搜索测试。我将姓氏和频率百分比插入表中。我添加了一个列并用一个整数填充它,这个整数是“total names required*frequency”的乘积。人口普查的频率数据加起来并不是100%,所以我的名字总数也有点不足。我可以通过从列表中随机选择名字并增加他们的计数来更正这个数字,直到我得到了所需的数字,随机添加的计数从未超过1000万总数的0.05%。

    我生成了1到88799之间的1000万个随机数。对于每个随机数,我将从列表中选择该名称,并减少该名称的计数器。我的方法是模拟处理一副牌,除了我的牌有很多不同的牌和每一张牌的不同数目。

        4
  •  0
  •   Orbling    15 年前

    你把实际频率和等级一起存储吗?

    如果您知道要使用什么值,那么将代数从公认的答案转换为MySQL就不麻烦了 n . y 会是你现在拥有的 ROUND(88799 * RAND(), 0) 和 x0,x1 = 1,88799 我想,虽然我可能误解了。从T-SQL的角度来看,唯一涉及的非标准数学运算符是 ^ 只是 POWER(x,y) == x^y .

    推荐文章