0

I want to generate a random number from range [a,b] that is dividsible by N (4 in my case). I have the solution, but is there a better (more elegant) way to do it?

result = random.randint(a, b)
result = math.ceil(result / 4) * 4

Solutions from here: Python: Generate random number between x and y which is a multiple of 5 doesn't answer my question since I'll have to implement something like:

random.randint(a, b) * 4;

I'll have to divide original range by 4 and it's less readable then my original solution

KocT9H
  • 299
  • 1
  • 4
  • 14
  • 2
    Your solution is wrong. For e.g. if a=1, b=15, your solution may give 16 which is wrong – Sabareesh May 14 '19 at 05:53
  • 2
    "a number that divides by N" isn't legal English, and "a number that divides N" means the other thing. I think you mean **"a number that is divisible by N"** – smci May 14 '19 at 05:55
  • @DYZ please see my comment regarding duplicate solution you've mentioned – KocT9H May 14 '19 at 07:08

4 Answers4

2

A generic solution and an example

import random

def divisible_random(a,b,n):
    if b-a < n:
      raise Exception('{} is too big'.format(n))
    result = random.randint(a, b)
    while result % n != 0:
      result = random.randint(a, b)
    return result
# get a random int in the range 2 - 12, the number is divisible by 3
print(divisible_random(2,12,3))
balderman
  • 22,927
  • 7
  • 34
  • 52
1

The first thing coming to my mind is creating a list of all the possible choices using range in the given interval, followed by randomly choosing one value using choice.

So, in this case, for a given a and b,

random.choice(range(a + 4 - (a%4), b, 4))

If a is a perfect multiple of 4, then

random.choice(range(a, b, 4))

Would give you the required random number.

So, in a single generic function, (as suggested in comments)

def get_num(a, b, x):
    if not a % x:
        return random.choice(range(a, b, x))
    else:
        return random.choice(range(a + x - (a%x), b, x))

where x is the number whose multiples are required.

Divyanshu Srivastava
  • 1,379
  • 11
  • 24
1

Use random.randrange with a step size of n, using a+n-(a%n) as start if a is non-divisible by n, else use a as start

import random

def rand_n(a, b,n):

    #If n is bigger than range, return -1
    if n > b-a:
        return -1

    #If a is divisible by n, use a as a start, using n as step size
    if a%n == 0:
        return random.randrange(a,b,n)

    # If a is not divisible by n, use a+n-(a%n) as a start, using n as step size
    else:
        return random.randrange(a+n-(a%n),b, n)
Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
1

As the others have pointed out, your solution might produce out of range results, e.g. math.ceil(15 / 4) * 4 == 16. Also, be aware that the produced distribution might be very far from uniform. For example, if a == 0 and b == 4, the generated number will be 4 in 80% of the cases. Aside from that, it seems good to me, but in Python, you can also just use the integer division operator (actually floor division, so it's not equivalent to your examlpe):

result = random.randint(a, b)
result = result // 4 * 4

But a more general albeit less efficient method of generating uniform random numbers with specific constraints (while also keeping the uniform distribution) is generating them in a loop until you find a good one:

result = 1
while result % 4 != 0:
    result = random.randint(a, b)
szmate1618
  • 1,545
  • 1
  • 17
  • 21