我基本上需要答案 这个SO问题提供了幂律分布, ,为我翻译成 T-SQL。

我想从某个人中一次提取一个姓氏 人口普查提供的姓名表. 。我想要得到与人口中的分布大致相同的分布。该表有 88,799 个名字,按出现频率排列。“Smith”排名第 1,出现频率为 1.006%,“Alderink”排名第 88,799,出现频率为 1.7 x 10^-6。“Sanders”排名 75,出现频率为 0.100%。

曲线根本不必精确拟合。只需给我大约百分之一的“史密斯”和大约百万分之一的“奥尔德林克”

这是我到目前为止所拥有的。

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

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

我保证,当一个更聪明的人做出回应时,我仍然会尝试自己解决这个问题。

有帮助吗?

解决方案

当您可以从实际分布中得出结论时,为什么要满足幂律分布呢?

我建议您更改 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

即选择数字不超过[均匀分布]随机数的名字。注意查询如何使用 少于 并订购 描述-结束订单;这将保证第一个条目(史密斯)被选中。另一种选择是以 10,060 而不是零开始史密斯系列赛,并丢弃小于该值的随机抽签。

除了上面提到的边界管理问题(从零开始而不是 10,060 开始)之外,此解决方案以及迄今为止的其他两个响应与中建议的解决方案相同 迪麦基对此问题中引用的问题的回答。本质上这个想法是使用 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 个名字,从 Smith 到 Alvarado
  • 第二个第三 = 6,277 个名字,从 到 Gainer
  • 最后三分之一 = 82,097 个名字,从弗里斯比到最后

比如说,如果我们需要 1,000 个名字,我们会从列表的前三分之一中抽取 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)

其他提示

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

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

我将问题理解为“我需要获得一连串的名字,以反映1990年美国人口普查中姓氏的频率”

我对这个问题的理解可能与其他建议略有不同,尽管已经接受了一个答案,但很透彻的回答是,我将根据普查的姓氏贡献自己的经验。

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

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

您将实际频率与等级一起存储吗?

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top