For example I would like to generate 22 numbers between 20 and 46 with an average value of 27. And I would like the numbers to cover the range as well as possible.
E
You could sove this in a pure mathematical way.
if the amount of needed numbers is no even - just select one of the numbers as the average itself and then you will need to generate k-1 numbers (which is even)
** EDIT **
you can use this code that manipulates an initial list to meet the requirements of a average so the resulting list is not symmetrical. (Better than generating lists until you hit the correct one)
import random
def generate_numbers(wanted_avg, numbers_to_generate, start, end):
rng = [i for i in range(start, end)]
initial_selection = [random.choice(rng) for _ in range(numbers_to_generate)]
initial_avg = reduce(lambda x, y: x+y, initial_selection) / float(numbers_to_generate)
print "initial selection is: " + str(initial_selection)
print "initial avg is: " + str(initial_avg)
if initial_avg == wanted_avg:
return initial_selection
off = abs(initial_avg - wanted_avg)
manipulation = off * numbers_to_generate
sign = -1 if initial_avg > wanted_avg else 1
manipulation_action = dict()
acceptable_indices = range(numbers_to_generate)
while manipulation > 0:
random_index = random.choice(acceptable_indices)
factor = manipulation_action[random_index] if random_index in manipulation_action else 0
after_manipulation = initial_selection[random_index] + factor + sign * 1
if start <= after_manipulation <= end:
if random_index in manipulation_action:
manipulation_action[random_index] += sign * 1
manipulation -= 1
else:
manipulation_action[random_index] = sign * 1
manipulation -= 1
else:
acceptable_indices.remove(random_index)
for key in manipulation_action:
initial_selection[key] += manipulation_action[key]
print "after manipulation selection is: " + str(initial_selection)
print "after manipulation avg is: " + str(reduce(lambda x, y: x+y, initial_selection) / float(numbers_to_generate))
return initial_selection
I used an iterative approach to continuously track the current avg and the target avg while generating the numbers. Every step is directed towards bringing the cur_avg towards the target while using the RNG/
import random
def gen_target_avg(num, min, max, avg):
numbers = []
sum = 0
cur_avg = max
print "Current avg is %f: %r" % (cur_avg, numbers)
for n in range(0, num):
if avg < cur_avg:
n = random.randint(min, cur_avg)
sum += n
numbers.append(n)
elif avg > cur_avg:
n = random.randint(cur_avg, max)
sum += n
numbers.append(n)
cur_avg = sum/len(numbers)
print "Final avg is %f: %r" % (cur_avg, numbers)
>>> gen_target_avg(100, 3, 150, 25)
Current avg is 150.000000: []
Final avg is 25.000000: [81, 47, 56, 58, 23, 27, 24, 3, 7, 11, 19, 15, 19, 11, 18, 14, 24, 20, 17, 3, 121, 16, 7, 24, 6, 14, 13]
>>> gen_target_avg(100, 3, 150, 25)
Current avg is 150.000000: []
Final avg is 25.000000: [56, 3, 14, 139, 10, 10, 11, 34, 22, 26, 31, 24, 16, 19, 11, 28, 28, 6, 25, 19, 25, 17, 21]
>>> gen_target_avg(100, 3, 150, 30)
Current avg is 150.000000: []
Final avg is 30.000000: [143, 131, 38, 54, 34, 75, 31, 11, 63, 42, 38, 4, 22, 46, 27, 13, 6, 17, 14, 6, 21, 15, 3, 30, 15, 29, 28, 4, 32, 9, 17, 22, 10, 28, 11, 26]
So I also used the random number generator. In addition, in order to meet the specification to cover the range as well as possible, I also calculated the standard deviation, which is a good measure of spread. So whenever a sample set meets the criteria of mean of 27, I compared it to previous matches and constantly choose the samples that have the highest std dev (mean always = 27). SO in my script, I did 5 trials, and you can see the output that the final answer matches the samples_list that had highest std dev. Note that Python 3.4 or higher is needed to use statistics module. If you're using Python 2, then you can replace the stdev function with of your own (which you can easily find on Google or Stackoverflow)
import random as rd
import statistics as st
min_val = 20
max_val = 46
sample_count = 22
expected_mean = 27
num_of_trials = 5
def get_samples_list(min_v, max_v, s_count, exp_mean):
target_sum = sample_count * expected_mean
samples_list = []
curr_stdev_max = 0
for trials in range(num_of_trials):
samples = [0] * sample_count
while sum(samples) != target_sum:
samples = [rd.randint(min_v, max_v) for trial in range(s_count)]
print ("Mean: ", st.mean(samples), "Std Dev: ", st.stdev(samples), )
print (samples, "\n")
if st.stdev(samples) > curr_stdev_max:
curr_stdev_max = st.stdev(samples)
samples_best = samples[:]
return samples_best
samples_list = get_samples_list(min_val, max_val, sample_count, expected_mean)
print ("\nFinal list: ",samples_list)
samples_list.sort()
print ("\nSorted Final list: ",samples_list)
Here is the output:
Mean: 27 Std Dev: 6.90755280213519
[34, 30, 39, 21, 23, 32, 22, 23, 22, 20, 27, 30, 29, 20, 32, 24, 42, 20, 39, 24, 20, 21]
Mean: 27 Std Dev: 6.07100838882165
[21, 21, 34, 27, 35, 22, 29, 34, 24, 21, 20, 22, 20, 23, 26, 29, 28, 31, 30, 41, 21, 35]
Mean: 27 Std Dev: 6.2105900340811875
[26, 27, 26, 26, 25, 42, 32, 23, 21, 34, 23, 20, 25, 25, 21, 27, 40, 21, 26, 26, 37, 21]
Mean: 27 Std Dev: 8.366600265340756
[27, 22, 22, 44, 41, 21, 28, 36, 21, 23, 21, 25, 20, 20, 39, 46, 23, 25, 21, 23, 20, 26]
Mean: 27 Std Dev: 6.347102826149446
[21, 30, 20, 41, 25, 23, 39, 26, 27, 20, 28, 23, 29, 24, 20, 40, 27, 27, 22, 25, 34, 23]
Final list: [27, 22, 22, 44, 41, 21, 28, 36, 21, 23, 21, 25, 20, 20, 39,
46, 23, 25, 21, 23, 20, 26]
Sorted Final list: [20, 20, 20, 21, 21, 21, 21, 22, 22, 23, 23, 23, 25, 25,
26, 27, 28, 36, 39, 41, 44, 46]
>>>
Not optimal in terms of covering the range as much as possible, but you can try this:
def GenerateArr(count,minimum,maximum,average):
arr = []
diff = 1
while len(arr) < count-1:
if minimum <= average-diff and average+diff <= maximum:
arr.append(average-diff)
arr.append(average+diff)
diff += 1
else:
arr.append(average)
diff = 1
if len(arr) < count:
arr.append(average)
return arr
print GenerateArr(22,20,46,27)
Warning: this is not the optimal solution but it works quite fast with your input parameters:
import random
def gen_avg(expected_avg=27, n=22, a=20, b=46):
while True:
l = [random.randint(a, b) for i in range(n)]
avg = reduce(lambda x, y: x + y, l) / len(l)
if avg == expected_avg:
return l
for i in range(100):
print gen_avg()
In my laptop will give 100 random sequences in ~2.5s:
[21, 20, 20, 23, 25, 24, 29, 22, 42, 23, 31, 26, 30, 25, 29, 38, 20, 31, 46, 28, 41, 20]
[20, 30, 39, 26, 24, 31, 35, 36, 22, 22, 20, 32, 23, 21, 42, 32, 23, 24, 26, 28, 29, 25]
[23, 43, 21, 31, 44, 24, 24, 20, 27, 31, 28, 22, 26, 33, 25, 30, 21, 26, 33, 20, 31, 25]
[36, 28, 24, 29, 32, 21, 36, 28, 24, 27, 24, 22, 28, 28, 33, 21, 20, 32, 23, 30, 35, 21]
[21, 34, 25, 32, 20, 37, 31, 20, 46, 25, 21, 25, 35, 36, 21, 26, 21, 35, 24, 21, 30, 28]
[28, 23, 43, 22, 20, 23, 30, 41, 25, 32, 20, 21, 21, 30, 26, 22, 46, 40, 27, 26, 26, 21]
[21, 26, 38, 27, 42, 36, 43, 21, 29, 32, 22, 29, 26, 20, 23, 22, 21, 38, 23, 21, 21, 23]
[28, 21, 27, 34, 38, 21, 25, 37, 27, 22, 36, 38, 27, 20, 32, 35, 20, 24, 31, 22, 24, 24]
[28, 31, 23, 26, 36, 23, 30, 36, 32, 23, 20, 25, 23, 25, 25, 40, 21, 27, 21, 25, 28, 38]
[30, 24, 24, 25, 38, 27, 35, 31, 21, 31, 27, 22, 25, 25, 39, 31, 22, 23, 30, 29, 20, 23]
[25, 21, 42, 24, 24, 31, 22, 34, 32, 21, 23, 27, 20, 35, 31, 23, 26, 33, 20, 23, 22, 43]
[23, 23, 20, 40, 27, 22, 22, 41, 20, 21, 45, 26, 22, 28, 29, 39, 24, 23, 26, 34, 20, 38]
[22, 44, 27, 36, 29, 22, 24, 35, 30, 20, 20, 31, 38, 23, 20, 31, 25, 29, 30, 21, 24, 28]
[27, 30, 21, 28, 39, 30, 33, 33, 25, 21, 26, 33, 33, 24, 31, 20, 24, 25, 22, 27, 28, 20]
[23, 28, 24, 20, 35, 29, 33, 22, 23, 21, 31, 39, 32, 22, 24, 39, 22, 21, 29, 32, 34, 25]
[38, 25, 20, 22, 21, 44, 32, 20, 26, 20, 21, 29, 25, 20, 37, 27, 33, 23, 20, 39, 28, 27]
[29, 22, 30, 20, 31, 23, 23, 24, 28, 28, 46, 27, 24, 23, 21, 35, 20, 21, 45, 29, 27, 34]
[21, 29, 21, 33, 38, 44, 21, 20, 41, 30, 26, 23, 23, 29, 21, 32, 20, 29, 32, 29, 21, 26]
[21, 20, 24, 40, 30, 30, 31, 27, 20, 34, 28, 37, 31, 20, 38, 43, 25, 25, 21, 20, 27, 22]
[27, 35, 30, 21, 36, 37, 41, 21, 23, 24, 23, 32, 23, 25, 20, 23, 36, 40, 30, 24, 20, 24]
[39, 30, 30, 21, 26, 23, 20, 25, 36, 22, 23, 29, 23, 42, 20, 23, 32, 20, 30, 36, 25, 31]
[24, 24, 25, 41, 20, 27, 35, 31, 21, 24, 31, 36, 20, 25, 22, 32, 31, 30, 21, 22, 33, 25]
[22, 34, 33, 20, 29, 24, 34, 27, 27, 31, 20, 24, 22, 33, 36, 22, 21, 31, 21, 38, 21, 44]
[20, 43, 29, 23, 22, 32, 20, 21, 26, 23, 26, 33, 34, 22, 26, 20, 38, 21, 29, 32, 24, 33]
[23, 38, 39, 35, 21, 33, 25, 39, 33, 28, 23, 20, 22, 22, 25, 30, 20, 26, 28, 31, 24, 30]
[21, 36, 32, 42, 25, 25, 20, 46, 24, 22, 22, 22, 33, 32, 22, 29, 24, 26, 30, 23, 22, 21]
[25, 34, 20, 23, 44, 21, 40, 21, 25, 20, 26, 25, 44, 20, 24, 32, 21, 23, 31, 21, 43, 22]
[34, 31, 23, 20, 25, 30, 22, 23, 30, 22, 31, 21, 41, 42, 21, 22, 36, 20, 26, 24, 31, 32]
[27, 27, 35, 34, 31, 36, 21, 21, 24, 39, 22, 29, 29, 20, 20, 46, 23, 30, 27, 24, 28, 21]
[28, 37, 30, 31, 38, 23, 21, 20, 21, 32, 29, 24, 32, 20, 20, 21, 28, 20, 32, 41, 25, 40]
[27, 25, 24, 46, 23, 24, 26, 28, 30, 23, 23, 27, 38, 20, 44, 21, 27, 25, 20, 23, 26, 28]
[20, 28, 24, 33, 25, 22, 28, 27, 22, 27, 42, 25, 43, 27, 20, 26, 29, 33, 29, 24, 36, 23]
[39, 31, 27, 20, 23, 25, 22, 22, 24, 26, 25, 42, 21, 22, 21, 21, 28, 24, 44, 31, 35, 34]
[20, 20, 31, 30, 41, 30, 40, 20, 28, 20, 21, 25, 23, 30, 28, 25, 29, 21, 28, 28, 38, 24]
[35, 37, 30, 20, 24, 25, 36, 21, 23, 29, 25, 23, 21, 27, 22, 44, 41, 24, 33, 30, 22, 20]
[22, 22, 25, 20, 22, 29, 25, 25, 21, 33, 23, 20, 21, 42, 39, 32, 33, 30, 46, 34, 21, 22]
[26, 25, 27, 35, 30, 39, 20, 26, 43, 20, 29, 33, 23, 27, 35, 21, 20, 36, 24, 27, 21, 28]
[20, 30, 24, 31, 36, 23, 24, 46, 30, 28, 24, 25, 24, 22, 20, 33, 20, 21, 24, 23, 39, 27]
[20, 22, 33, 22, 33, 22, 38, 24, 29, 36, 23, 31, 26, 24, 28, 28, 26, 28, 43, 24, 25, 20]
[36, 26, 24, 24, 24, 33, 21, 28, 27, 29, 31, 31, 21, 27, 25, 24, 34, 20, 33, 37, 23, 30]
[25, 33, 24, 27, 30, 35, 25, 30, 23, 23, 20, 20, 28, 30, 29, 38, 25, 25, 32, 21, 25, 33]
[30, 21, 27, 33, 21, 43, 23, 23, 25, 29, 23, 38, 28, 24, 21, 44, 22, 31, 32, 21, 20, 27]
[21, 33, 26, 26, 33, 27, 26, 32, 41, 31, 25, 29, 33, 22, 26, 38, 24, 22, 21, 28, 21, 21]
[26, 28, 31, 20, 38, 20, 26, 23, 24, 24, 30, 21, 21, 33, 26, 33, 38, 28, 40, 24, 21, 26]
[21, 27, 30, 22, 24, 28, 38, 27, 20, 23, 41, 20, 26, 41, 26, 24, 29, 22, 20, 43, 21, 42]
[22, 28, 21, 25, 25, 46, 29, 27, 24, 34, 24, 27, 23, 23, 34, 21, 37, 26, 20, 44, 28, 24]
[32, 27, 21, 35, 28, 37, 21, 32, 23, 20, 21, 20, 31, 21, 32, 24, 35, 26, 42, 35, 22, 24]
[22, 36, 41, 35, 27, 20, 25, 21, 28, 32, 31, 30, 23, 26, 23, 26, 28, 33, 23, 35, 20, 23]
[28, 24, 27, 39, 37, 25, 21, 29, 36, 27, 24, 24, 26, 27, 37, 27, 28, 23, 27, 21, 22, 26]
[32, 33, 24, 31, 22, 35, 39, 20, 39, 39, 22, 21, 23, 28, 34, 23, 20, 21, 35, 24, 23, 22]
[25, 20, 20, 25, 28, 23, 37, 30, 34, 27, 22, 20, 41, 22, 22, 23, 42, 27, 39, 25, 22, 24]
[20, 36, 22, 21, 21, 38, 20, 45, 36, 28, 23, 23, 35, 26, 20, 30, 31, 28, 33, 22, 25, 31]
[30, 23, 21, 45, 20, 31, 35, 21, 22, 24, 29, 32, 34, 30, 23, 31, 20, 20, 31, 35, 29, 20]
[22, 21, 22, 32, 42, 24, 35, 22, 26, 21, 28, 23, 24, 21, 31, 20, 27, 39, 42, 21, 37, 33]
[26, 28, 24, 20, 42, 23, 22, 20, 22, 33, 24, 25, 24, 28, 38, 26, 44, 28, 31, 20, 28, 25]
[25, 42, 32, 20, 25, 20, 27, 22, 21, 20, 31, 28, 21, 31, 25, 25, 46, 28, 30, 41, 25, 20]
[20, 27, 28, 26, 34, 23, 20, 22, 34, 20, 36, 30, 21, 23, 26, 23, 31, 37, 23, 34, 40, 29]
[28, 30, 22, 21, 20, 35, 20, 24, 23, 24, 34, 33, 37, 34, 24, 21, 24, 21, 41, 32, 22, 30]
[21, 20, 21, 28, 24, 42, 36, 26, 29, 33, 26, 22, 33, 28, 26, 31, 20, 22, 29, 32, 27, 28]
[23, 41, 24, 39, 22, 23, 25, 21, 24, 35, 20, 27, 31, 43, 34, 29, 24, 22, 23, 21, 37, 21]
[20, 29, 32, 21, 27, 25, 32, 36, 24, 34, 33, 31, 25, 21, 29, 26, 21, 32, 23, 33, 20, 29]
[21, 32, 22, 26, 40, 40, 23, 28, 26, 25, 31, 21, 20, 37, 30, 30, 25, 35, 27, 24, 24, 21]
[25, 25, 27, 21, 27, 23, 20, 39, 20, 43, 29, 21, 38, 27, 30, 23, 24, 29, 20, 24, 36, 30]
[26, 20, 22, 25, 28, 22, 21, 29, 30, 37, 23, 39, 20, 34, 33, 23, 25, 30, 27, 41, 27, 26]
[29, 20, 32, 22, 26, 27, 21, 20, 27, 26, 22, 42, 28, 24, 26, 23, 40, 24, 27, 42, 23, 44]
[21, 38, 25, 26, 35, 28, 21, 40, 37, 28, 25, 23, 26, 22, 22, 21, 46, 29, 23, 23, 21, 32]
[39, 23, 20, 37, 26, 22, 30, 21, 25, 25, 32, 20, 39, 20, 33, 23, 24, 32, 38, 22, 33, 27]
[21, 21, 21, 20, 34, 20, 27, 36, 25, 20, 20, 26, 34, 40, 32, 32, 30, 22, 28, 29, 36, 35]
[37, 30, 23, 20, 21, 38, 26, 21, 41, 34, 33, 26, 36, 26, 25, 34, 34, 20, 24, 21, 20, 25]
[28, 24, 25, 29, 41, 24, 24, 29, 34, 37, 20, 29, 21, 30, 21, 28, 33, 20, 22, 43, 27, 20]
[35, 36, 20, 29, 26, 30, 26, 26, 34, 22, 24, 25, 21, 35, 34, 34, 25, 21, 23, 31, 24, 20]
[24, 25, 22, 22, 31, 28, 38, 42, 33, 24, 37, 27, 21, 42, 22, 28, 25, 20, 21, 25, 22, 29]
[26, 38, 33, 40, 22, 22, 21, 26, 29, 26, 23, 25, 35, 21, 27, 23, 35, 21, 43, 29, 20, 29]
[26, 31, 25, 20, 20, 21, 25, 25, 24, 28, 22, 30, 22, 22, 40, 37, 29, 46, 20, 38, 22, 24]
[24, 26, 23, 22, 34, 26, 30, 20, 24, 21, 32, 24, 24, 31, 31, 27, 28, 31, 32, 45, 25, 33]
[30, 32, 24, 27, 24, 21, 25, 22, 23, 39, 39, 22, 27, 23, 37, 23, 32, 23, 38, 21, 36, 22]
[27, 23, 22, 24, 28, 42, 26, 33, 28, 20, 20, 35, 22, 20, 31, 27, 34, 23, 32, 36, 23, 38]
[31, 35, 23, 28, 21, 28, 37, 20, 38, 34, 27, 20, 24, 27, 22, 26, 20, 29, 35, 31, 28, 21]
[28, 21, 33, 31, 24, 29, 20, 23, 25, 22, 28, 27, 30, 25, 29, 28, 26, 22, 38, 39, 30, 33]
[23, 36, 41, 26, 23, 29, 29, 45, 20, 23, 34, 21, 22, 39, 35, 24, 20, 24, 24, 25, 23, 28]
[25, 25, 21, 32, 44, 24, 32, 28, 33, 21, 22, 38, 32, 22, 25, 29, 22, 27, 29, 26, 26, 32]
[35, 28, 35, 24, 27, 34, 25, 30, 25, 42, 29, 26, 20, 36, 25, 25, 20, 20, 23, 21, 31, 34]
[44, 23, 20, 25, 24, 28, 20, 23, 38, 21, 24, 28, 35, 30, 20, 45, 23, 28, 39, 26, 23, 25]
[22, 25, 22, 45, 21, 22, 31, 37, 42, 28, 21, 24, 26, 25, 30, 25, 20, 36, 23, 25, 23, 29]
[32, 21, 28, 20, 20, 41, 25, 20, 46, 21, 38, 31, 25, 26, 21, 30, 20, 31, 39, 24, 22, 24]
[34, 24, 23, 26, 33, 20, 26, 26, 24, 20, 24, 25, 21, 29, 31, 30, 39, 34, 28, 27, 25, 41]
[26, 36, 20, 27, 23, 23, 20, 27, 32, 23, 24, 36, 34, 23, 24, 43, 25, 25, 33, 24, 31, 31]
[23, 21, 28, 20, 33, 46, 20, 43, 21, 24, 28, 21, 22, 34, 38, 25, 20, 20, 20, 25, 44, 32]
[30, 35, 26, 21, 28, 20, 21, 22, 24, 35, 23, 22, 21, 26, 24, 31, 41, 37, 27, 20, 43, 25]
[34, 42, 21, 20, 26, 21, 40, 20, 41, 21, 21, 23, 29, 22, 21, 26, 30, 25, 32, 29, 39, 27]
[31, 44, 40, 27, 22, 21, 44, 21, 30, 26, 20, 34, 22, 20, 20, 20, 29, 36, 22, 21, 22, 38]
[21, 43, 46, 28, 32, 22, 23, 21, 24, 21, 28, 21, 20, 33, 21, 24, 36, 22, 23, 28, 45, 32]
[21, 22, 24, 39, 21, 25, 25, 20, 23, 20, 22, 26, 40, 39, 22, 28, 41, 38, 20, 20, 33, 33]
[34, 22, 26, 23, 28, 20, 32, 25, 24, 20, 39, 28, 41, 24, 29, 32, 24, 33, 23, 34, 24, 23]
[22, 39, 36, 22, 24, 20, 28, 23, 30, 39, 23, 33, 37, 25, 27, 20, 23, 27, 34, 31, 25, 20]
[34, 20, 20, 22, 26, 21, 26, 40, 23, 20, 42, 23, 31, 25, 46, 31, 27, 28, 22, 31, 34, 23]
[24, 29, 39, 24, 39, 26, 25, 25, 29, 23, 27, 20, 46, 21, 27, 25, 20, 41, 24, 31, 25, 22]
[42, 25, 23, 30, 22, 23, 37, 20, 29, 32, 28, 22, 35, 36, 22, 20, 27, 39, 21, 20, 22, 32]
[24, 26, 37, 44, 26, 23, 21, 23, 34, 33, 36, 22, 22, 22, 21, 27, 35, 24, 22, 44, 21, 20]
[24, 27, 22, 30, 20, 27, 21, 24, 31, 22, 30, 33, 35, 24, 23, 20, 32, 28, 30, 44, 26, 29]
Here is a Python solution to this question. This one has the following advantages:
It's based on an algorithm by John McClane, which he posted as an answer to another question. I describe the algorithm in another answer.
import random # Or secrets
def permTable(nc, sum, mn, mx):
t=[[0 for i in range(sum+1)] \
for j in range(nc+1)]
t[0][0]=1
for i in range(1,nc+1):
for j in range(0,sum+1):
jm=max(j-(mx-mn), 0)
v=0
for k in range(jm, j+1): v+=t[i-1][k]
t[i][j]=v
return t
def getSol(nc, sum, mn, mx, table):
s=sum-nc*mn
ret=[0 for i in range(nc)]
for ib in range(nc):
i=nc-1-ib
# Or 'secrets.randint(table[i+1][s])'
v=random.randint(0, table[i+1][s]-1)
r=mn
v-=table[i][s]
while v>=0:
v-=table[i][s-1]
s-=1
r+=1
ret[i]=r
return ret
def genAverage(numSamples, valuesPerSample, mn, mx, avg):
sum = avg * valuesPerSample
pt = permTable(valuesPerSample, sum, mn, mx)
return [getSol(valuesPerSample, sum, mn, mx, pt) \
for i in range(numSamples)]
# Print one sample
print(genAverage(1, 22, 20, 46, 27))
# Print 100 samples
print(genAverage(100, 22, 20, 46, 27))