Generate N positive integers in a range, summing up to a total in python

I saw other posts regarding a similar issue. I know how to generate N positive integers. I also know how to limit the amount of randomly generated integers. The only problem is to satisfy the condition that none of the values ​​of N fall out of the specified range.

eg. generate_ints(n, total, low, high)should generate an n array of values ​​so that each value is between low and high, and the sum is summed to total. Any pointers / help would be greatly appreciated.

for example generate_ints(4, 40, 4, 15)should generate something like

[7,10,13,10]

I don’t care if the numbers are repeated if they are not very distorted. I use np.randon.randint(5,15,n)to select an integer.

So far I have tried the following, but this did not work -

import numpy as np 
import random 
from random import uniform as rand 

total=50 
n=10 
low=2 
high=15 
result=[] 
m=0 
nobs=1 
while nobs <= n: 
    if m >= (total - low): 
        last_num= total -new_tot 
        result.append(last_num) 
    else: 
        next_num=np.random.randint(low,high,1) 
        new_tot = sum(result) + next_num 
        result.append(next_num) 
        m=new_tot 
    nobs +=1 

print result 
print sum(result)

Thanks again.

+4
source share
3 answers
import numpy as np

def sampler(samples, sum_to , range_list):
    assert range_list[0]<range_list[1], "Range should be a list, the first element of which is smaller than the second"
    arr = np.random.rand(samples)
    sum_arr = sum(arr)

    new_arr = np.array([int((item/sum_arr)*sum_to) if (int((item/sum_arr)*sum_to)>range_list[0]and int((item/sum_arr)*sum_to)<range_list[1]) \
                            else np.random.choice(range(range_list[0],range_list[1]+1)) for item in arr])
    difference = sum(new_arr) - sum_to
    while difference != 0:
        if difference < 0 :
                for idx in np.random.choice(range(len(new_arr)),abs(difference)):
                    if new_arr[idx] != range_list[1] :
                        new_arr[idx] +=  1

        if difference > 0:
                for idx in np.random.choice(range(len(new_arr)), abs(difference)):
                    if new_arr[idx] != 0 and new_arr[idx] != range_list[0] :
                        new_arr[idx] -= 1
        difference = sum(new_arr) - sum_to
    return new_arr

new_arr = sampler (2872,30000,[5,15])
print "Generated random array is :"
print new_arr
print "Length of array:", len(new_arr)
print "Max of array: ", max(new_arr)
print "min of array: ", min(new_arr)
print "and it sums up to %d" %sum(new_arr)

result:

Generated random array is :
[ 9 10  9 ...,  6 15 11]
Length of array: 2872
Max of array:  15
min of array:  5
and it sums up to 30000
0
source

Here is my attempt, which I will explain.

import numpy as np


def generate_ints(n, total, low, high):
    begin = 0
    randys = []
    correctTotal = False
    while correctTotal is False:
        while begin < n:
            r1 = np.random.randint(low, high, 1)
            randys.append(r1)
            begin += 1
        if sum(randys) == total:
            correctTotal = True
        else:
            begin = 0
            del randys[:]
    generated_list = np.array(randys).tolist()
    gen = [g[0] for g in generated_list]
    return gen


my_list = generate_ints(4, 40, 4, 15)
print "Generated list '{}' with sum {}.".format(my_list, sum(my_list))

Inside the function, I set two constants randysand begin. In the inner loop while, while beginsmaller n, it generates nrandom integers between lowand high. If the sum is equivalent total, exit the outer loop while, otherwise it needs to reset the constants.

A simple return randyswill give a list of NumPy arrays. Using the method tolist(), a list is created instead.

. , . return .

HTH.

0

, , ,

partition, . k-. , , , , . Python:

import functools
import random

@functools.lru_cache(1 << 10)
def C1(n, k, a, b):
    "Counts the compositions of `n` into `k` parts bounded by `a` and `b`" 
    return C2(n - k*(a - 1), k, b - (a - 1))

def C2(n, k, b):
    "Computes C(n, k, 1, b) using binomial coefficients"
    total = 0
    sign = +1

    for i in range(0, k + 1):
        total += sign * choose(k, i) * choose(n - i*b - 1, k - 1)
        sign = -sign

    return total

def choose(n, k):
    "Computes the binomial coefficient of (n, k)"
    if k < 0 or k > n:
        return 0

    if k == 0 or k == n:
        return 1

    k = min(k, n - k)
    c = 1

    for i in range(k):
        c = c * (n - i) // (i + 1)

    return c

def check_pre_and_post_conditions(f):
    def wrapper(n, k, a, b):
        assert 1 <= k <= n, (n, k)
        assert 1 <= a <= b <= n, (n, a, b)
        assert k*a <= n <= k*b, (n, k, a, b)

        comp = f(n, k, a, b)

        assert len(comp) == k, (len(comp), k, comp)
        assert sum(comp) == n, (sum(comp), n, comp)
        assert all(a <= x <= b for x in comp), (a, b, comp)

        return comp
    return functools.wraps(f)(wrapper)

@check_pre_and_post_conditions
def random_restricted_composition(n, k, a, b):
    "Produces a random composition of `n` into `k` parts bounded by `a` and `b`"
    total = C1(n, k, a, b)
    which = random.randrange(total)
    comp = []

    while k:
        for x in range(a, min(b, n) + 1):
            count = C1(n - x, k - 1, a, b)

            if count > which:
                break

            which -= count

        comp.append(x)
        n -= x
        k -= 1

    return comp

, , , , i-th (. ). .

, C1(n, k, a, b) , n k. .

0

Source: https://habr.com/ru/post/1658759/


All Articles