Зачем соглашаться на степенное распределение, когда вы можете извлечь из фактического распределения?
Я предлагаю вам изменить таблицу LastNames, включив в нее числовой столбец, который будет содержать числовое значение, представляющее фактическое число индивидов с более распространенным именем. Вы, вероятно, захотите число в меньшей, но пропорциональной шкале, скажем, возможно, 10000 для каждого процента представительства.
Список будет выглядеть примерно так:
(кроме трех имен, упомянутых в вопросе, я предполагаю, что Уайт, Джонсон и др.)
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, а не с нуля, и отбросить случайные ничьи, меньшие, чем это значение.
Помимо упомянутого выше вопроса об управлении границами (начиная с нуля, а не 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)
Это запрос из вопроса, за исключением последней строки.
Re-Edit
При рассмотрении фактического распределения, как видно из данных переписи, кривая является чрезвычайно крутой и потребует очень большой коэффициент мощности, что, в свою очередь, приведет к переполнению и / или крайним ошибкам округления в наивной формуле показано выше.
Более разумный подход может состоять в том, чтобы работать на нескольких уровнях, то есть выполнять одинаковое количество розыгрышей в каждой из, скажем, трех третей (или четырех четвертей или ...) совокупного распределения; в каждом из этих списков частей мы будем рисовать с использованием степенной функции, возможно, с тем же коэффициентом, но с разными диапазонами.
Например
Если взять трети, список делится следующим образом:
- Первая треть = 425 имен, от Смита до Альварадо
- Второй третий = 6 277 имен, от Гейнера
- Последняя треть = 82 097 имен, от Фрисби до конца
Если бы нам понадобилось, скажем, 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)