我基本上需要this SO question that provides a power-law distribution的答案,为我翻译成T-SQL。
我想从census provided table of names一次拉一个姓氏,一次一个。我希望获得与人口中大致相同的分布。该表有88,799个名称按频率排名。 “史密斯”排名第一,频率为1.006%,“Alderink”排名为88,799,频率为1.7×10 ^ -6。 “桑德斯”排名第75,频率为0.100%。
曲线根本不必精确拟合。只要给我约1%的“史密斯”和大约百万分之一的“Alderink”
这是我到目前为止所拥有的。
SELECT [LastName]
FROM [LastNames] as LN
WHERE LN.[Rank] = ROUND(88799 * RAND(), 0)
但这当然会产生统一的分布。
我保证,在聪明人回应的时候,我仍然会试图弄明白这一点。
答案 0 :(得分:3)
我建议你改变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开始而不是10,060),此解决方案以及目前为止的其他两个响应与 dmckee 中建议的相同回答这个问题中引用的问题。基本上这个想法是使用CDF( 累积分布函数 )。
修改强>:
如果你坚持使用数学函数而不是实际分布,下面应该提供一个幂律函数,它会以某种方式传达真实分布的“长尾”形状。你可能想调整@PwrCoef值(BTW不必是整数),本质上系数越大,函数列表开头的偏差就越大。
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)
除了最后一行之外,问题的查询是什么。
<强>重新修改:
在查看实际分布时,如人口普查数据所示,曲线非常陡峭并且需要非常大的功率系数,这反过来会导致天真的溢出和/或极端的舍入错误如上所示的公式。
更合理的方法可以是在几个层次中操作,即在累积分布的每个(例如三分之三(或四分之三或......))中执行相同数量的抽取;在每个零件清单中,我们将使用幂律函数绘制,可能具有相同的系数,但具有不同的范围。
例如
假设三分之一,列表分为:
如果我们需要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)
答案 1 :(得分:2)
不是将pdf存储为等级,而是存储CDF(从Aldekirk开始,直到该名称的所有频率之和)。
然后修改您的选择以检索排名大于公式结果的第一个LN。
答案 2 :(得分:1)
我读到的问题是“我需要获得一系列名称来反映1990年美国人口普查中姓氏的频率”
我可能已经阅读了与其他建议略有不同的问题,虽然答案已被接受,而且答案非常明确,我将通过人口普查的姓氏贡献我的经验。
我从1990年的人口普查中下载了相同的数据。我的目标是在医疗记录应用程序的性能测试期间生成大量要提交用于搜索测试的名称。我将姓氏和频率百分比插入表中。我添加了一个列,并用一个整数填充,该整数是“所需的总名称*频率”的乘积。人口普查的频率数据并没有达到100%,因此我的名字总数也略低于要求。我能够通过从列表中选择随机名称并增加其数量来更正数字,直到我确切地获得所需的数字,随机添加的数量从未超过总数1000万的0.05%。
我在1到88799的范围内生成了1000万个随机数。每个随机数我会从列表中选择该名称并减少该名称的计数器。我的方法是模拟处理一副牌,除了我的牌组有更多不同的牌和每张牌的变数。
答案 3 :(得分:0)
您是否将实际频率存储在等级中?
如果你知道n
使用什么值,那么将代数从接受的答案转换为MySQL就不会有任何麻烦。 y
将是你现在所拥有的ROUND(88799 * RAND(), 0)
和x0,x1 = 1,88799
我认为,尽管我可能会误解它。从T-SQL角度来看,唯一的非标准数学运算符是^
,它只是POWER(x,y) == x^y
。