简体   繁体   中英

Generate random value from a range of number for a fixed limit in python

code:

 import random
 import numpy as np
 X=10
 Y=5
 while True:
    random_value=np.random.choice((range(1,X)),Y)
    if sum(random_value)==X:
       break
    else:
       continue

Question

This code works fine for small value of X and Y but it does not work for big values like X=56 and Y= 52. My aim is to generate random values from X in a list whose size is Y and also the sum of the random value should b X

How should i optimise the code to get output for big values

Here's some working code. In a case like X = 56 and Y = 52 , most of the numbers will be ones, so this code figures out how many 1s are needed, generates the other numbers, and then subtracts from or adds to those random numbers until they sum to the needed sum.

import numpy as np
import random

def get_vals_from_X_Y(X, Y):
    # If X = 56 and Y = 55, can have a single 2 in generated values and then 54 ones.
    # If X = 56 and Y = 54, can have 2 twos and 52 ones.
    # Pattern is that you need (Y - (X - Y)) ones, which simplifies to (2Y - X).
    # This pattern ONLY APPLIES if Y >= X / 2. If Y < X / 2, then every Y value needs to be >= 2.

    if Y >= X / 2:
        num_1s_needed = 2*Y - X
    else:
        num_1s_needed = 0
    
    # vals will eventually contain Y values summing to X.
    vals = []
    vals = [1]*num_1s_needed

    # What the random numbers need to sum to. It's X minus all the ones.
    sum_needed = X - num_1s_needed
    
    if sum_needed == 0:
        return vals
    elif sum_needed == 1:
        idx = random.randrange(num_1s_needed-1)
        vals.insert(idx, 1)
        return vals
    else:
        # places_left determines how many random numbers to generate.
        places_left = Y - num_1s_needed
        random_vals = [random.randrange(1, sum_needed) for i in range(places_left)]
        
        # If sum of random_vals is too small, add randomly to a value as needed.
        while sum(random_vals) < sum_needed:
            idx_to_add_to = random.randrange(len(random_vals))
            if random_vals[idx_to_add_to] < X:
                random_vals[idx_to_add_to] += 1
        
        # Sort from big to small and find the first instance of 1.
        # Add all 1s to vals and remove from random_vals.
        # Adjust sum_needed to always be X - len(vals).

        random_vals.sort(reverse=True)
        try:
            idx_1 = random_vals.index(1)
            orig_length = len(random_vals)
            random_vals = random_vals[:idx_1]
            vals += [1]*(orig_length - len(random_vals))
            sum_needed -= (orig_length - len(random_vals))

        # If there is no 1 in random_vals, function will go to the except block.
        except:
            pass
        
        # Subtract as needed from a random value that is greater than 1.
        while sum(random_vals) > sum_needed:
            idx_to_subtract_from = random.randrange(len(random_vals))
            if random_vals[idx_to_subtract_from] > 1:
                random_vals[idx_to_subtract_from] -= 1
        
        # Generate a random index and insert each random_val into vals at that index.
        for i in range(len(random_vals)):
            idx = 0
            if len(vals) > 1:
                idx = random.randrange(len(vals))
            vals.insert(idx, random_vals[i])
        return vals

# Change X and Y to your liking.
vals = get_vals_from_X_Y(X, Y)

Let me know if anything needs clarification.

You can sample from a suitable multinomial distribution , but the given range [1..limit-1] will usually not be exhausted in the result, even if it were possible.

import numpy as np
rng = np.random.default_rng()

N     = 52
limit = 56
a = rng.multinomial(limit - N, np.full(N, 1.0/N)) + 1

print(a)
print(f'sum:{a.sum()}, length:{a.shape[0]}')

Output (random)

[1 2 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1]
sum:56, length:52

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM