Implement Rand10() Using Rand7()
MedPrompt
Given the API rand7() that generates a uniform random integer in the range [1, 7], write a function rand10() that generates a uniform random integer in the range [1, 10]. You can only call the API rand7(), and you shouldn't call any other API. Please do not use a language's built-in random API.
Each test case will have one internal argument n, the number of times that your implemented function rand10() will be called while testing. Note that this is not an argument passed to rand10().
Example 1:
Input: n = 1
Output: [2]Example 2:
Input: n = 2
Output: [2,8]Example 3:
Input: n = 3
Output: [3,8,10]
Constraints:
1 <= n <= 105
Follow up:
- What is the expected value for the number of calls to
rand7()function? - Could you minimize the number of calls to
rand7()?
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses two calls to rand7() to generate a number in a larger range, and then rejects values that fall outside a usable sub-range that is a multiple of 10.
Algorithm
- Start an infinite loop.
- Generate a number
idxfrom 1 to 49 using the formula:idx = (rand7() - 1) * 7 + rand7(). This requires two calls torand7(). - Check if
idxis within the usable range[1, 40]. - If
idx <= 40, the number is accepted. Calculate the result as(idx - 1) % 10 + 1and return it. - If
idx > 40, the number is rejected. The loop continues, and we try again from step 2.
Walkthrough
The core idea is to generate a number in a range larger than 10 and then map it down. A single call to rand7() gives 7 outcomes, which is not enough to map to 10 uniform outcomes. Two calls to rand7() give 7 * 7 = 49 equally likely outcomes. We can map these 49 outcomes to numbers from 1 to 49. A common way is to use a base-7 mapping: (rand7() - 1) * 7 + rand7(). This generates a uniform random integer in the range [1, 49]. To get a uniform rand10(), we need a range that is a multiple of 10. We can use the numbers from 1 to 40 from our generated range [1, 49]. If the generated number is between 1 and 40 (inclusive), we can map it to a number between 1 and 10. The formula (number - 1) % 10 + 1 achieves this uniformly. If the generated number is greater than 40 (i.e., 41 to 49), we "reject" this result and repeat the entire process by calling rand7() twice again. This is necessary to maintain uniformity, as we cannot map the 9 leftover numbers (41-49) to 10 outcomes fairly. This process is repeated until a valid number (1-40) is generated.
/** * The rand7() API is already defined in the parent class SolBase. * public int rand7(); * @return a random integer in the range 1 to 7 */class Solution extends SolBase { public int rand10() { while (true) { int row = rand7(); int col = rand7(); int idx = (row - 1) * 7 + col; // Generates a uniform number in [1, 49] if (idx <= 40) { return (idx - 1) % 10 + 1; } } }}Complexity
Time
O(1) on average. The expected number of calls to `rand7()` is constant. The probability of accepting a number in one iteration is `40/49`. The expected number of iterations is `1 / (40/49) = 49/40 = 1.225`. Since each iteration makes 2 calls to `rand7()`, the expected number of `rand7()` calls is `2 * 1.225 = 2.45`.
Space
O(1). We only use a few variables to store the intermediate results.
Trade-offs
Pros
Relatively simple to understand and implement.
Correctly produces a uniform distribution for
rand10().
Cons
It's not the most efficient in terms of the number of
rand7()calls. It discards the information from the rejected numbers (41-49), leading to more calls on average.
This approach improves upon the basic rejection sampling by reusing the information from the rejected numbers to reduce the average number of calls to rand7().
Algorithm
- Start an infinite loop.
- First Level: Generate
num1 = (rand7() - 1) * 7 + rand7(). This gives a uniform number in[1, 49]. - If
num1 <= 40, return(num1 - 1) % 10 + 1. - Second Level: We have a rejected number from
[41, 49]. This is arand9(). Letval9 = num1 - 40. - Generate
num2 = (val9 - 1) * 7 + rand7(). This gives a uniform number in[1, 63]. - If
num2 <= 60, return(num2 - 1) % 10 + 1. - Third Level: We have a rejected number from
[61, 63]. This is arand3(). Letval3 = num2 - 60. - Generate
num3 = (val3 - 1) * 7 + rand7(). This gives a uniform number in[1, 21]. - If
num3 <= 20, return(num3 - 1) % 10 + 1. - Restart: The number was 21. This is a
rand1(). We have no more randomness to extract, so we continue the loop from the beginning.
Walkthrough
This method starts the same way as the basic approach: generate a number idx from 1 to 49. If idx is in [1, 40], we accept it and return (idx - 1) % 10 + 1. The key difference is what happens when idx > 40. Instead of discarding the result and starting over, we recognize that we have just generated a uniform random number in the range [41, 49]. This is equivalent to generating a uniform random number from 1 to 9 (by subtracting 40). Let's call this rand9(). Now, the problem is to generate rand10() given a rand9() and the rand7() function. We can combine our rand9() result with a new call to rand7() to generate an even larger range. Using the formula (rand9() - 1) * 7 + rand7(), we can generate a uniform random number in the range [1, 63]. This only required one additional rand7() call. From this new range [1, 63], we can use the numbers [1, 60]. If our number falls in this range, we return (number - 1) % 10 + 1. If the number is in [61, 63], we have generated a rand3(). We can repeat the process again: combine this rand3() with a new rand7() call to get a rand21(). From [1, 21], we can use [1, 20]. If the number is in this range, we return the result. If the number is 21, we have a rand1(), which provides no new randomness. At this point, we have exhausted the utility of the rejected values and must restart the entire process from the beginning. This cascading use of rejected ranges significantly reduces the probability of needing to start over completely, thus lowering the expected number of rand7() calls.
/** * The rand7() API is already defined in the parent class SolBase. * public int rand7(); * @return a random integer in the range 1 to 7 */class Solution extends SolBase { public int rand10() { while (true) { // First attempt: generate rand49, use [1, 40] int num49 = (rand7() - 1) * 7 + rand7(); if (num49 <= 40) { return (num49 - 1) % 10 + 1; } // Reuse the rejected range [41, 49] -> [1, 9] int num9 = num49 - 40; // Second attempt: combine rand9 with rand7 to get rand63, use [1, 60] int num63 = (num9 - 1) * 7 + rand7(); if (num63 <= 60) { return (num63 - 1) % 10 + 1; } // Reuse the rejected range [61, 63] -> [1, 3] int num3 = num63 - 60; // Third attempt: combine rand3 with rand7 to get rand21, use [1, 20] int num21 = (num3 - 1) * 7 + rand7(); if (num21 <= 20) { return (num21 - 1) % 10 + 1; } // Rejected [21], must restart } }}Complexity
Time
O(1) on average. The expected number of calls to `rand7()` is lower than the basic approach. The probability of success with 2 calls is `40/49`. The probability of success with 3 calls is `(9/49) * (60/63)`. The probability of success with 4 calls is `(9/49) * (3/63) * (20/21)`. The expected number of calls is approximately `2.19`, which is a significant improvement over `2.45`.
Space
O(1). Only a few variables are needed.
Trade-offs
Pros
More efficient in terms of the expected number of
rand7()calls, minimizing waste.Guarantees a uniform distribution.
Cons
The implementation is more complex and less intuitive than the basic rejection sampling method.
Solutions
Solution
/** * The rand7() API is already defined in the parent class SolBase. * public int rand7(); * @return a random integer in the range 1 to 7 */ class Solution extends SolBase { public int rand10 () { while ( true ) { int i = rand7 () - 1 ; int j = rand7 (); int x = i * 7 + j ; if ( x <= 40 ) { return x % 10 + 1 ; } } } }// The rand7() API is already defined for you. // int rand7(); // @return a random integer in the range 1 to 7 class Solution { public: int rand10 () { while ( 1 ) { int i = rand7 () - 1 ; int j = rand7 (); int x = i * 7 + j ; if ( x <= 40 ) { return x % 10 + 1 ; } } } };# The rand7() API is already defined for you. # def rand7(): # @return a random integer in the range 1 to 7 class Solution : def rand10 ( self ): """ :rtype: int """ while 1 : i = rand7 () - 1 j = rand7 () x = i * 7 + j if x <= 40 : return x % 10 + 1Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.