就像发布的其他答案一样,因为我们知道长度,我们可以通过只关注总和来获得平均值。
我会递归地解决它。在基本情况下,我们需要生成一个长度为 1 的列表,其总和为某个数字 s。这很简单:列表只包含s:
rand 1 s = [s]
现在我们可以解决递归情况rand n s,其中n 是所需的列表长度,s 是所需的总和。为此,我们将生成两个列表 x 和 y 并将它们连接在一起,但要遵守给定的约束:
length x + length y = n
sum x + sum y = s
1 * length x <= sum x -- Minimum value is 1
10 * length x >= sum x -- Maximum value is 10
1 * length y <= sum y
10 * length y >= sum y
这些方程/不等式还不能求解,所以我们要做的第一件事就是选择列表的长度。为了降低递归级别,我们可以选择lx = round (n / 2),然后设置以下内容:
length x = lx
length y = n - lx = ly
因此:
sum x + sum y = s
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= sum y
10 * ly >= sum y
我们用第一个等式重写不等式:
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= s - sum x
10 * ly >= s - sum x
我们可以重新排列底部的两个以使sum x成为主题:
sum x + 1 * ly <= s
sum x + 10 * ly >= s
sum x <= s - 1 * ly
sum x >= s - 10 * ly
我们知道ly 和s,所以这些为我们提供了sum x 的明确界限,我们通过取最大下限和最小上限来组合:
max (1 * lx) (s - 10 * ly) <= sum x
min (10 * lx) (s - 1 * ly) >= sum x
这些界限是有意义的:它们考虑了x 中的每个元素都是 1 或 10 的情况并且它们确保余数可以由 sum y 处理。现在我们只需在这些边界之间生成一个随机数B,然后设置:
sum x = B
sum y = s - B
由此,我们可以执行递归(假设某个随机数函数randInt):
rand n s = let lx = round (n / 2)
ly = n - lx
lower = max (1 * lx) (s - 10 * ly)
upper = min (10 * lx) (s - 1 * ly)
b = randInt lower upper
in rand lx b ++ rand ly (s - b)
现在可以通过调用生成您的列表:
myList = rand 100 700
为了简洁起见,我用 Haskell 编写了这个,但它只是算术,所以应该很容易转换为 C#。如果有帮助,这里是一个 Python 版本:
def rand(n, s):
if n == 1:
return [s]
lx = int(n / 2)
ly = n - lx
lower = max(1 * lx, s - 10 * ly)
upper = min(10 * lx, s - 1 * ly)
b = randint(lower, upper)
result = rand(lx, b)
result.extend(rand(ly, s - b))
return result
请指出我犯的任何错误!
编辑:虽然我怀疑 C# 的情况,但在某些语言中,我们可以通过使用 tail-recursion 使这更简单、更高效。首先我们切换到一次生成一个元素:
-- Generate one number then recurse
rand 1 s = [s]
rand n s = let ly = n - 1
lower = max 1 (s - 10 * ly)
upper = min 10 (s - 1 * ly)
x = randInt lower upper
in x : rand (n - 1) s
然后我们累积结果而不是建立未完成的延续:
rand' xs 1 s = s:xs
rand' xs n s = let ly = n - 1
lower = max 1 (s - 10 * ly)
upper = min 10 (s - 1 * ly)
x = randInt lower upper
in rand' (x:xs) (n-1) s
rand = rand' []