我提出以下查询(PostgreSQL):
WITH ZeroToOne (m, n, y) AS (
SELECT m, n, random()
FROM inputs
CROSS JOIN generate_series(1, n)
), SumToM (m, n, y, x) AS (
SELECT m, n, y, y * m / sum(y) OVER (PARTITION BY m, n)
FROM zerotoone
), MissingToM (m, n, l) AS (
SELECT m, n, m - sum(floor(x))
FROM sumtom
GROUP BY m, n
)
SELECT m, n, y, x, l,
CASE
WHEN row_number() OVER (PARTITION BY m, n ORDER BY x - floor(x) DESC) > l
THEN floor(x)
ELSE ceil(x)
END AS v
FROM missingtom
NATURAL JOIN sumtom;
唯一有趣的值是 m、n 和 v;出于解释目的,我保留了其他值。
我将使用以下输入案例作为运行示例逐步完成查询:
SELECT * FROM inputs;
m | n
----+---
20 | 4
30 | 4
42 | 3
(3 rows)
第一个 CTE (ZeroToOne) 为每个输入案例计算 [0, 1] 范围内的 n 随机值,并将这些值称为 y:
m | n | y
----+---+---------------------
20 | 4 | 0.374425032641739
20 | 4 | 0.644279096741229
20 | 4 | 0.626386553514749
20 | 4 | 0.320786282420158
30 | 4 | 0.848764919675887
30 | 4 | 0.268079651053995
30 | 4 | 0.250213726423681
30 | 4 | 0.497460773680359
42 | 3 | 0.571454062592238
42 | 3 | 0.00338772451505065
42 | 3 | 0.139226260595024
第二个 CTE (SumToM) 将每个 y 值乘以 m,然后将结果除以输入案例的值之和。因此,将输入对 (m, n) 的所有x 相加得到m:
m | n | y | x
----+---+---------------------+-------------------
20 | 4 | 0.374425032641739 | 3.80924177094873
20 | 4 | 0.644279096741229 | 6.55462277759638
20 | 4 | 0.626386553514749 | 6.37259161753762
20 | 4 | 0.320786282420158 | 3.26354383391728
30 | 4 | 0.848764919675887 | 13.6565766414436
30 | 4 | 0.268079651053995 | 4.3133855037604
30 | 4 | 0.250213726423681 | 4.02592384820881
30 | 4 | 0.497460773680359 | 8.00411400658722
42 | 3 | 0.571454062592238 | 33.6117414945302
42 | 3 | 0.00338772451505065 | 0.199258922297338
42 | 3 | 0.139226260595024 | 8.18899958317244
很明显,m 大于x 值的整数部分之和。也很容易看出两个和之间的差(x 值的总和和 x 值的整数部分的总和)小于n。所以现在的想法是计算 hom 有多少数字必须四舍五入,多少数字必须四舍五入。第三个CTE(MissingToM)的l值是要四舍五入的值的个数:
m | n | l
----+---+---
20 | 4 | 2
30 | 4 | 1
42 | 3 | 1
为确保数字的分布保持均匀,我们将具有最高小数部分的数字与最终查询一起四舍五入:
m | n | y | x | l | v
----+---+---------------------+-------------------+---+----
20 | 4 | 0.374425032641739 | 3.80924177094873 | 2 | 4
20 | 4 | 0.644279096741229 | 6.55462277759638 | 2 | 7
20 | 4 | 0.626386553514749 | 6.37259161753762 | 2 | 6
20 | 4 | 0.320786282420158 | 3.26354383391728 | 2 | 3
30 | 4 | 0.848764919675887 | 13.6565766414436 | 1 | 14
30 | 4 | 0.268079651053995 | 4.3133855037604 | 1 | 4
30 | 4 | 0.250213726423681 | 4.02592384820881 | 1 | 4
30 | 4 | 0.497460773680359 | 8.00411400658722 | 1 | 8
42 | 3 | 0.571454062592238 | 33.6117414945302 | 1 | 34
42 | 3 | 0.00338772451505065 | 0.199258922297338 | 1 | 0
42 | 3 | 0.139226260595024 | 8.18899958317244 | 1 | 8
由于输入表中多次出现相同的配置(m,n),查询会失败,所以我在其上添加一个主键约束:
ALTER TABLE inputs ADD PRIMARY KEY (m, n);