生成随机数列表,使用 SQL 求和到固定数量
Generating a list of random numbers, summing to a fixed amount using SQL
这道题是用SQL生成N个和为M(常数)的随机数。
例如我们有一个数字 M=9.754。我们需要 10 个随机数,其总和为 9.754.
应该在SQL服务器环境下完成。
有人能帮帮我吗?
有趣的要求。
下面的查询使用计数table / 数字table 生成10 个随机数,然后找到比率。最终查询检查数字之和不等于@m 的情况,并对最大数字进行调整。
declare @m decimal(10,3) = 9.754,
@n int = 10;
with
-- using recursive CTE to generate a number table
numbers as
(
select n = 1
union all
select n = n + 1
from numbers
where n < @n
),
-- generate random positive numbers using newid()
-- Note : 100 is chosen arbitrary
num as
(
select n = abs(checksum(newid())) % 100
from numbers
),
-- calculate the ratio
ratio as
(
select r,
rn = row_number() over (order by r desc),
sum_r = sum(r) over()
from
(
select r = convert(decimal(10,3), n * 1.0 / sum(n) over() * @m)
from num
) r
)
-- sum(r) may not equal to @m due to rounding
-- find the difference and adjust it to the biggest r
select r, rn, sum_r,
adj_r = r + case when rn = 1 then @m - sum_r else 0 end,
sum_adj_r = sum(r + case when rn = 1 then @m - sum_r else 0 end) over()
from ratio
虽然@Squirrel 的回答很有趣,但这里的数字更随机
这是代码:
DECLARE @s INT=1,
@k FLOAT=0,
@final FLOAT=9.917,
@sum FLOAT =0,
@min FLOAT=1,
@max FLOAT=9.917
BEGIN
WHILE (@sum <> @final)
BEGIN
WHILE (@s <= 10)
BEGIN
SET @k =
(
SELECT ROUND(RAND(CHECKSUM(NEWID())) * (@max - @min) + @min,3)
);
PRINT (CONCAT('random: ',@k));
IF(@sum+@k <=@final)
SET @sum+=@k;
SET @max=@final-@sum;
PRINT (CONCAT('computed sum: ',@k));
IF(@max>1) SET @min=1 ELSE SET @min=0;
IF(@sum=@final)
BREAK;
SET @s = @s + 1;
SET @k = @k + 0;
END;
PRINT (CONCAT('final', @final))
PRINT (CONCAT('sum', @sum))
IF(@sum<>@final)--force stop if after 10 try the sum not match with final
BEGIN
PRINT(CONCAT('final random number:',@final-@sum))
SET @sum=@final;
END;
SET @s=0;
IF(@sum=@final)
BEGIN
PRINT('****************************DONE****************************')
BREAK;
END
END;
PRINT ('end');
END;
这道题是用SQL生成N个和为M(常数)的随机数。
例如我们有一个数字 M=9.754。我们需要 10 个随机数,其总和为 9.754.
应该在SQL服务器环境下完成。
有人能帮帮我吗?
有趣的要求。
下面的查询使用计数table / 数字table 生成10 个随机数,然后找到比率。最终查询检查数字之和不等于@m 的情况,并对最大数字进行调整。
declare @m decimal(10,3) = 9.754,
@n int = 10;
with
-- using recursive CTE to generate a number table
numbers as
(
select n = 1
union all
select n = n + 1
from numbers
where n < @n
),
-- generate random positive numbers using newid()
-- Note : 100 is chosen arbitrary
num as
(
select n = abs(checksum(newid())) % 100
from numbers
),
-- calculate the ratio
ratio as
(
select r,
rn = row_number() over (order by r desc),
sum_r = sum(r) over()
from
(
select r = convert(decimal(10,3), n * 1.0 / sum(n) over() * @m)
from num
) r
)
-- sum(r) may not equal to @m due to rounding
-- find the difference and adjust it to the biggest r
select r, rn, sum_r,
adj_r = r + case when rn = 1 then @m - sum_r else 0 end,
sum_adj_r = sum(r + case when rn = 1 then @m - sum_r else 0 end) over()
from ratio
虽然@Squirrel 的回答很有趣,但这里的数字更随机 这是代码:
DECLARE @s INT=1,
@k FLOAT=0,
@final FLOAT=9.917,
@sum FLOAT =0,
@min FLOAT=1,
@max FLOAT=9.917
BEGIN
WHILE (@sum <> @final)
BEGIN
WHILE (@s <= 10)
BEGIN
SET @k =
(
SELECT ROUND(RAND(CHECKSUM(NEWID())) * (@max - @min) + @min,3)
);
PRINT (CONCAT('random: ',@k));
IF(@sum+@k <=@final)
SET @sum+=@k;
SET @max=@final-@sum;
PRINT (CONCAT('computed sum: ',@k));
IF(@max>1) SET @min=1 ELSE SET @min=0;
IF(@sum=@final)
BREAK;
SET @s = @s + 1;
SET @k = @k + 0;
END;
PRINT (CONCAT('final', @final))
PRINT (CONCAT('sum', @sum))
IF(@sum<>@final)--force stop if after 10 try the sum not match with final
BEGIN
PRINT(CONCAT('final random number:',@final-@sum))
SET @sum=@final;
END;
SET @s=0;
IF(@sum=@final)
BEGIN
PRINT('****************************DONE****************************')
BREAK;
END
END;
PRINT ('end');
END;