【问题标题】:Power-law distribution in T-SQLT-SQL 中的幂律分布
【发布时间】:2011-05-15 07:50:55
【问题描述】:

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

我想从census provided table of names 中提取一个姓氏,一次一个。我想获得与人口中大致相同的分布。该表有 88,799 个按频率排列的名字。 “Smith”排名 1,频率为 1.006%,“Alderink”排名 88,799,频率为 1.7 x 10^-6。 “桑德斯”排名第 75 位,出现频率为 0.100%。

曲线根本不需要精确拟合。给我大约 1% 的“Smith”和大约百万分之一的“Alderink”

这是我目前所拥有的。

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

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

我保证等到更聪明的人做出回应时,我仍然会自己弄清楚这一点。

【问题讨论】:

    标签: sql tsql random statistics power-law


    【解决方案1】:

    我将问题解读为“我需要获取一系列姓名,这将反映 1990 年美国人口普查中姓氏的频率”

    我可能对这个问题的解读与其他建议有所不同,虽然答案已被接受,而且答案非常透彻,但我将贡献我对人口普查姓氏的经验。

    我从 1990 年的人口普查中下载了相同的数据。我的目标是在医疗记录应用程序的性能测试期间生成大量要提交用于搜索测试的姓名。我将姓氏和频率百分比插入到表格中。我添加了一列并用一个整数填充它,该整数是“所需的总名称 * 频率”的乘积。来自人口普查的频率数据加起来并不完全是 100%,所以我的姓名总数也有点不足。我能够通过从列表中选择随机名称并增加它们的计数来更正这个数字,直到我得到完全需要的数字,随机添加的计数从未超过 1000 万总数的 0.05%。

    我生成了 1 到 88799 范围内的 1000 万个随机数。对于每个随机数,我会从列表中选择该名称并递减该名称的计数器。我的方法是模拟处理一副牌,除了我的牌有更多不同的牌并且每张牌的数量不同。

    【讨论】:

      【解决方案2】:

      既然可以从实际分布中得出结论,为什么还要满足于幂律分布?

      我建议您更改 LastNames 表以包含一个数字列,该列将包含一个数值,该数值表示具有更常见名称的个人的实际数量。您可能需要一个较小但成比例的数字,例如,每个百分比的代表可能有 10,000 个。

      然后列表将如下所示:
      (除了问题中提到的 3 个名字,我猜是 White、Johnson 等人)

      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
      

      这是选择不超过[均匀分布]随机数的名字。请注意查询如何使用 小于 并以 desc 结尾的顺序进行排序;这将保证第一个条目(史密斯)被选中。另一种方法是从 10,060 而不是 0 开始与 Smith 的系列,并丢弃小于该值的随机抽奖。

      除了上面提到的边界管理问题(从 0 开始而不是 10,060)之外,这个解决方案以及到目前为止的其他两个响应与 dmckee 中建议的解决方案相同'对这个问题中提到的问题的回答。本质上,这个想法是使用 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 个名字,从史密斯到阿尔瓦拉多
      • 第二个第三个 = 6,277 个名字,从获得者到获得者
      • 最后三分之一 = 82,097 个名字,从 Frisby 到最后

      如果我们需要 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)
      

      【讨论】:

      • 我喜欢这两种选择。这个简单的数学函数对我来说更简单,但如果分布准确性变得更重要,我将使用您回复第一部分中的想法。感谢您提供所有详细信息。
      【解决方案3】:

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

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

      【讨论】:

      • pdf = 主要分布频率?和 CDF = 累积分布频率?我想我跟着,即使我不懂行话。
      • @DanO 差不多,概率密度函数和累积分布函数。但你明白了。
      【解决方案4】:

      您是否将实际频率与排名一起存储?

      如果您知道n 使用什么值,那么将代数从接受的答案转换为 MySQL 并不麻烦。 y 将是您目前拥有的 ROUND(88799 * RAND(), 0)x0,x1 = 1,88799 我认为,尽管我可能会误解它。从 T-SQL 的角度来看,唯一涉及的非标准数学运算符是 ^,也就是 POWER(x,y) == x^y

      【讨论】:

        猜你喜欢
        • 2011-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-27
        • 1970-01-01
        相关资源
        最近更新 更多