Like the other answers have posted, since we know the length we can get the average by just focusing on the total sum.
I'd solve it recursively. In the base case, we need to generate a list of length 1 which sums to some number s
. That's easy: the list just contains s
:
rand 1 s = [s]
Now we can solve the recursive case rand n s
where n
is the desired list length and s
is the desired sum. To do this, we'll generate two lists x
and y
and concatenate them together, subject to the given constraints:
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
These equations/inequalities can't be solved yet, so the first thing we do is choose the length of the lists. To keep down the level of recursion we can choose lx = round (n / 2)
then set the following:
length x = lx
length y = n - lx = ly
Hence:
sum x + sum y = s
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= sum y
10 * ly >= sum y
We use the first equation to rewrite the inequalities:
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= s - sum x
10 * ly >= s - sum x
We can rearrange the bottom two to make sum x
the subject:
sum x + 1 * ly <= s
sum x + 10 * ly >= s
sum x <= s - 1 * ly
sum x >= s - 10 * ly
We know ly
and s
so these give us definite bounds for sum x
, which we combine by taking the largest lower bound and the smallest upper bound:
max (1 * lx) (s - 10 * ly) <= sum x
min (10 * lx) (s - 1 * ly) >= sum x
These bounds make sense: they take into account the cases where every element in x
is 1 or 10 and they ensure that the remainder can be handled by sum y
. Now we just generate a random number B
between these bounds, then set:
sum x = B
sum y = s - B
From that, we can perform our recursion (assuming some random number function 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)
Now your list can be generated by calling:
myList = rand 100 700
I've written this in Haskell for brevity, but it's just arithmetic so should translate to C# easily. Here's a Python version if it helps:
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
Please point out any mistakes I've made!
Edit: Although I doubt it's the case for C#, in some languages we could make this simpler and more efficient by using tail-recursion. First we switch to generating one element at a time:
-- 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
Then we accumulate the result rather than building up unfinished continuations:
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' []