Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate three random integers that satisfy some condition? [closed]

I'm a beginner in programming and I'm looking for a nice idea how to generate three integers that satisfy a condition.

Example:

We are given n = 30, and we've been asked to generate three integers a, b and c, so that 7*a + 5*b + 3*c = n. I tried to use for loops, but it takes too much time and I have a maximum testing time of 1000 ms.

I'm using Python 3.

My attempt:

x = int(input())
c = []
k = []
w = []
for i in range(x):
    for j in range(x):
        for h in range(x):
           if 7*i + 5*j + 3*h = x:
              c.append(i)
              k.append(j)
              w.append(h)
if len(c) == len(k) == len(w) 
    print(-1)
else: 
    print(str(k[0]) + ' ' + str(c[0]) + ' ' + str(w[0]))
like image 884
Colton Walker Avatar asked Oct 11 '20 17:10

Colton Walker


People also ask

How do you generate a random number in Python with conditions?

Another method to generate a floating random number is by using the random() function. The random() function does not take any arguments. It will generate a floating-point random number between the range 0 and 1 where 1 is not included in the output. The range is defined as: [0.0, 1.0).

How do you get a random integer in a range in C++?

How to Generate Random Numbers in C++ Within a Range. Similar to 1 and 10, you can generate random numbers within any range using the modulus operator. For instance, to generate numbers between 1 and 100, you can write int random = 1+ (rand() % 100).

How to generate random integer in Python?

The random module renders two primary built-in functions in Python to generate random integers, namely randint () and randrange (). In addition to these two functions, the article adds module secrets, which users can use to generate integers ranging from 0 to 9.

How to generate a list of random numbers within a range?

Given lower and upper limits, generate a given count of random numbers within a given range, starting from ‘start’ to ‘end’ and store them in list. Input : num = 10, start = 20, end = 40 Output : [23, 20, 30, 33, 30, 36, 37, 27, 28, 38] The output contains 10 random numbers in range [20, 40].

How do you solve a degenerate equation with random distribution?

If you wanted a slightly less degenerate solution, you could pick a random integer k (using any distribution of your choice) and return a = − n, b = n + 3 k, c = n − 5 k. As noted above, this is also a solution to the equation for any k. Of course, this distribution is still somewhat degenerate, since the value of a is fixed.

How to generate randoam numbers using NumPy random array?

randint accepts two parameters: a starting point and an ending point. Both should be integers and the first value should always be less than the second. Method 2: Using numpy random.randint () method to generate randoam numbers.


3 Answers

First, let me note that your task is underspecified in at least two respects:

  1. The allowed range of the generated values is not specified. In particular, you don't specify whether the results may include negative integers.
  2. The desired distribution of the generated values is not specified.

Normally, if not specified, one might assume that a uniform distribution on the set of possible solutions to the equation was expected (since it is, in a certain sense, the most random possible distribution on a given set). But a (discrete) uniform distribution is only possible if the solution set is finite, which it won't be if the range of results is unrestricted. (In particular, if (a, b, c) is a solution, then so is (a, b + 3k, c − 5k) for any integer k.) So if we interpret the task as asking for a uniform distribution with unlimited range, it's actually impossible!


On the other hand, if we're allowed to choose any distribution and range, the task becomes trivial: just make the generator always return a = −n, b = n, c = n. Clearly this is a solution to the equation (since −7n + 5n + 3n = (−7 + 5 + 3)n = 1n), and a degenerate distribution that assigns all probability mass to single point is still a valid probability distribution!

If you wanted a slightly less degenerate solution, you could pick a random integer k (using any distribution of your choice) and return a = −n, b = n + 3k, c = n − 5k. As noted above, this is also a solution to the equation for any k. Of course, this distribution is still somewhat degenerate, since the value of a is fixed.

If you want to let all return values be at least somewhat random, you could also pick a random h and return a = −n + h, b = n − 2h + 3k and c = n + h − 5k. Again, this is guaranteed to be a valid solution for any h and k, since it clearly satisfies the equation for h = k = 0, and it's also easy to see that increasing or decreasing either h or k will leave the value of the left-hand side of the equation unchanged.

In fact, it can be proved that this method can generate all possible solutions to the equation, and that each solution will correspond to a unique (h, k) pair! (One fairly intuitive way to see this is to plot the solutions in 3D space and observe that they form a regular lattice of points on a 2D plane, and that the vectors (+1, −2, +1) and (0, +3, −5) span this lattice.) If we pick h and k from some distribution that (at least in theory) assigns a non-zero probability to every integer, then we'll have a non-zero probability of returning any valid solution. So, at least for one somewhat reasonable interpretation of the task (unbounded range, any distribution with full support) the following code should solve the task efficiently:

from random import gauss

def random_solution(n):
    h = int(gauss(0, 1000))  # any distribution with full support on the integers will do
    k = int(gauss(0, 1000))
    return (-n + h, n - 2*h + 3*k, n + h - 5*k)

If the range of possible values is restricted, the problem becomes a bit trickier. On the positive side, if all values are bounded below (or above), then the set of possible solutions is finite, and so a uniform distribution exists on it. On the flip side, efficiently sampling this uniform distribution is not trivial.

One possible approach, which you've used yourself, is to first generate all possible solutions (assuming there's a finite number of them) and then sample from the list of solutions. We can do the solution generation fairly efficiently like this:

  1. find all possible values of a for which the equation might have a solution,
  2. for each such a, find all possible values of b for which there still have a solution,
  3. for each such (a, b) pair, solve the equation for c and check if it's valid (i.e. an integer within the specified range), and
  4. if yes, add (a, b, c) to the set of solutions.

The tricky part is step 2, where we want to calculate the range of possible b values. For this, we can make use of the observation that, for a given a, setting c to its smallest allowed value and solving the equation gives an upper bound for b (and vice versa).

In particular, solving the equation for a, b and c respectively, we get:

  • a = (n − 5b − 3c) / 7
  • b = (n − 7a − 3c) / 5
  • c = (n − 7a − 5b) / 3

Given lower bounds on some of the values, we can use these solutions to compute corresponding upper bounds on the others. For example, the following code will generate all non-negative solutions efficiently (and can be easily modified to use a lower bound other than 0, if needed):

def all_nonnegative_solutions(n):
    a_min = b_min = c_min = 0
    a_max = (n - 5*b_min - 3*c_min) // 7
    for a in range(a_min, a_max + 1):
        b_max = (n - 7*a - 3*c_min) // 5
        for b in range(b_min, b_max + 1):
            if (n - 7*a - 5*b) % 3 == 0:
                c = (n - 7*a - 5*b) // 3
                yield (a, b, c)

We can then store the solutions in a list or a tuple and sample from that list:

from random import choice

solutions = tuple(all_nonnegative_solutions(30))
a, b, c = choice(solutions)

Ps. Apparently Python's random.choice is not smart enough to use reservoir sampling to sample from an arbitrary iterable, so we do need to store the full list of solutions even if we only want to sample from it once. Or, of course, we could always implement our own sampler:

def reservoir_choice(iterable):
    r = None
    n = 0
    for x in iterable:
        n += 1
        if randrange(n) == 0:
           r = x
    return r

a, b, c = reservoir_choice(all_nonnegative_solutions(30))

BTW, we could make the all_nonnegative_solutions function above a bit more efficient by observing that the (n - 7*a - 5*b) % 3 == 0 condition (which checks whether c = (n − 7a − 5b) / 3 is an integer, and thus a valid solution) is true for every third value of b. Thus, if we first calculated the smallest value of b that satisfies the condition for a given a (which can be done with a bit of modular arithmetic), we could iterate over b with a step size of 3 starting from that minimum value and skip the divisibility check entirely. I'll leave implementing that optimization as an exercise.

like image 70
Ilmari Karonen Avatar answered Oct 17 '22 15:10

Ilmari Karonen


import numpy as np


def generate_answer(n: int, low_limit:int, high_limit: int):
    while True:
        a = np.random.randint(low_limit, high_limit + 1, 1)[0]
        b = np.random.randint(low_limit, high_limit + 1, 1)[0]
        c = (n - 7 * a - 5 * b) / 3.0
        if int(c) == c and low_limit <= c <= high_limit:
            break

    return a, b, int(c)


if __name__ == "__main__":
    n = 30
    ans = generate_answer(low_limit=-5, high_limit=50, n=n)
    assert ans[0] * 7 + ans[1] * 5 + ans[2] * 3 == n
    print(ans)

If you select two of the numbers a, b, c, you know the third. In this case, I randomize ints for a, b, and I find c by c = (n - 7 * a - 5 * b) / 3.0.

Make sure c is an integer, and in the allowed limits, and we are done.

If it is not, randomize again.


If you want to generate all possibilities,

def generate_all_answers(n: int, low_limit:int, high_limit: int):
    results = []
    for a in range(low_limit, high_limit + 1):
        for b in range(low_limit, high_limit + 1):
            c = (n - 7 * a - 5 * b) / 3.0
            if int(c) == c and low_limit <= c <= high_limit:
                results.append((a, b, int(c)))

    return results
like image 36
Gulzar Avatar answered Oct 17 '22 16:10

Gulzar


If third-party libraries are allowed, you can use SymPy's diophantine.diop_linear linear Diophantine equations solver:

from sympy.solvers.diophantine.diophantine import diop_linear
from sympy import symbols
from numpy.random import randint

n = 30
N = 8 # Number of solutions needed

# Unknowns
a, b, c = symbols('a, b, c', integer=True)

# Coefficients
x, y, z = 7, 5, 3

# Parameters of parametric equation of solution
t_0, t_1 = symbols('t_0, t_1', integer=True)

solution = diop_linear(x * a + y * b + z * c - n)

if not (None in solution):
  for s in range(N):
    # -10000 and 10000 (max and min for t_0 and t_1)
    t_sub = [(t_0, randint(-10000, 10000)), (t_1, randint(-10000, 10000))]

    a_val, b_val, c_val = map(lambda t : t.subs(t_sub), solution)

    print('Solution #%d' % (s + 1))
    print('a =', a_val, ', b =', b_val, ', c =', c_val)
else:
  print('no solutions')

Output (random):

Solution #1
a = -141 , b = -29187 , c = 48984
Solution #2
a = -8532 , b = -68757 , c = 134513
Solution #3
a = 5034 , b = 30729 , c = -62951
Solution #4
a = 7107 , b = 76638 , c = -144303
Solution #5
a = 4587 , b = 23721 , c = -50228
Solution #6
a = -9294 , b = -106269 , c = 198811
Solution #7
a = -1572 , b = -43224 , c = 75718
Solution #8
a = 4956 , b = 68097 , c = -125049
like image 15
MrGeek Avatar answered Oct 17 '22 15:10

MrGeek