按百分比权重选择随机元素

时间:2019-01-04 12:16:23

标签: sql-server

假设我具有以下表格格式,如何在Microsoft SQL Server中使用函数/存储过程基于随机用户的百分比来选择一个随机用户。

请注意,它应该是可满足该行中任何随机数据的通用函数。

对于普通的javascript操作等,我可以为此找到库,但是在SQL Server中找不到内置的存储过程/函数。

user  percentage
-----------------
a     0.1
b     0.3
c     0.4
d     0.2

1 个答案:

答案 0 :(得分:3)

找到每个用户的每个概率区间,然后使用RAND()查找加权加权选择。

DECLARE @Random FLOAT = RAND()

;WITH Odds AS
(
    SELECT
        V.*
    FROM
        (VALUES
        ('A', 0.1),
        ('B', 0.3),
        ('C', 0.4),
        ('D', 0.2)
        ) V (UserCode, Percentage)
),
OddIntervals AS
(
    SELECT
        O.*,
        OddStart = SUM(O.Percentage) OVER (ORDER BY O.UserCode) - O.Percentage,
        OddsEnd = SUM(O.Percentage) OVER (ORDER BY O.UserCode)
    FROM
        Odds AS O
)
SELECT
    O.*
FROM
    OddIntervals AS O
WHERE
    @Random > O.OddStart AND
    @Random <= O.OddsEnd

OddsIntervals如下所示:

UserCode    Percentage  OddStart    OddsEnd
A           0.1         0.0         0.1
B           0.3         0.1         0.4
C           0.4         0.4         0.8
D           0.2         0.8         1.0

RAND()不返回1,因此最后一个间隔可能会有些不利。


如果您需要多次执行此操作,则可以使用以下脚本生成N个数量的随机数(花费4秒为我生成100k值)。您必须在每行上提供不同的种子才能获得不同的RAND()结果,因此我使用NEWID()作为种子。您只需将#Odds表替换为您的表即可执行。

DECLARE @AmountRandomValues INT = 100000

IF OBJECT_ID('tempdb..#RandomValues') IS NOT NULL
    DROP TABLE #RandomValues

SELECT TOP (@AmountRandomValues)
    RandomValue = RAND(CONVERT(VARBINARY, NEWID()))
INTO 
    #RandomValues
FROM 
    sys.columns AS s1
    CROSS JOIN sys.columns AS s2

IF OBJECT_ID('tempdb..#Odds') IS NOT NULL
    DROP TABLE #Odds

CREATE TABLE #Odds (
    UserCode CHAR(1),
    Percentage DECIMAL(3,2))

INSERT INTO #Odds (
    UserCode,
    Percentage)
VALUES
    ('A', 0.1),
    ('B', 0.3),
    ('C', 0.4),
    ('D', 0.2)

;WITH OddIntervals AS
(
    SELECT
        O.*,
        OddStart = SUM(O.Percentage) OVER (ORDER BY O.UserCode) - O.Percentage,
        OddsEnd = SUM(O.Percentage) OVER (ORDER BY O.UserCode)
    FROM
        #Odds AS O
)
SELECT
    R.RandomValue,
    O.*
FROM
    #RandomValues AS R
    INNER JOIN OddIntervals AS O ON 
        R.RandomValue > O.OddStart AND
        R.RandomValue <= O.OddsEnd

对于100k值,以下示例的几次运行用户选择量如下:

首次运行:

UserCode    Amount
A           10222
B           29883
C           39738
D           20157

第二次运行:

UserCode    Amount
A           10064
B           29794
C           40061
D           20081

第三次运行:

UserCode    Amount
A           10030
B           29960
C           40261
D           19749

您可以看到这与他们的赔率非常一致。