I’m creating a semi random array to control the position of targets within a game. I want them randomly distributed but I want a fixed total. Say I have an array of 8 numbers, I want each number to random between 1 and 4 and whole and I want the total to be 20. So [1,3,4,4,3,1,2,2] would be valid.
Initialize array with ones. Then add 1’s to random positions until total is reached.
var arr_size := 8
var total := 20
var arr: Array[int]
arr.resize(arr_size)
arr.fill(1)
for n in total - arr_size:
var i = randi_range(0, arr_size - 1)
while arr[i] == 4: # already maxed, try another index
i = randi_range(0, arr_size - 1)
arr[i] += 1
This is a good idea but i would initialize to a random int in range 1 to 2 or something instead. If all are initialized as 1, it feels like the number of ones will be much higher than had the numbers been more random.
Another solution that could lead to higher diversity in numbers would be to add all as random in range and then if total is greater than 20, -= 1 a random entry until the total is twenty.
You are correct about the distribution problem. This version starts with random values and nudges them until the required sum is reached. I think this is better distributed, but I’m not a statistician. But the results seem to be quite well distibuted when I tested with bigger values than 8 and 20.
var arr_size := 8
var total := 20
var arr: Array[int]
arr.resize(arr_size)
for i in arr_size:
arr[i] = randi_range(1, 4)
var sum := 0
for i in arr_size:
sum += arr[i]
while sum != total:
if sum > total:
# dec 1
var i = randi_range(0, arr_size - 1)
while arr[i] == 1: # 1 is min
i = randi_range(0, arr_size - 1)
arr[i] -= 1
sum -= 1
else:
# add 1
var i = randi_range(0, arr_size - 1)
while arr[i] == 4: # 4 is max
i = randi_range(0, arr_size - 1)
arr[i] += 1
sum += 1
It’s actually quite complicated to create a “general” solution to this problem. I’d be surprised if this isn’t on LeetCode, only with better defined constraints:
Are we only working with natural numbers?
Are we expecting duplicates?
Is the array sorted to begin with?
Do we need to include at least one of every number?
Will it always be possible to add the numbers up to the total? total=21, targets=[2, 4, 6, 8] or total=3, targets=[7, 12, 83, 41]
It’s an interesting exercise. My initial instinct was something like this:
func _get_targets(size: int, sum: int, targets: Array[int]) -> Array[int]:
var result: Array[int]
result.resize(size)
var min: int = targets.min()
result.fill(min)
sum -= min * size
targets.sort()
targets.pop_front()
# ... and then pick random stuff via targets.pick_random()
But then I got too into it and it got a bit annoying, trying to account for everything, so I gave up for now.
Thanks a lot. That worked nicely. I think the line to initially fill the array is missing but I’ve added one to do that and it works perfectly. Nice and adjustable too.