Random number generator in arbitrary probability distribution fashion

21 Oct, 2021
Given n numbers, each with some frequency of occurrence. Return a random number with probability proportional to its frequency of occurrence.
Example: 
Let following be the given numbers.
  arr[] = {10, 30, 20, 40}  

Let following be the frequencies of given numbers.
  freq[] = {1, 6, 2, 1}  

The output should be
  10 with probability 1/10
  30 with probability 6/10
  20 with probability 2/10
  40 with probability 1/10
It is quite clear that the simple random number generator won’t work here as it doesn’t keep track of the frequency of occurrence. 
We need to somehow transform the problem into a problem whose solution is known to us.
One simple method is to take an auxiliary array (say aux[]) and duplicate the numbers according to their frequency of occurrence. Generate a random number(say r) between 0 to Sum-1(including both), where Sum represents summation of frequency array (freq[] in above example). Return the random number aux[r] (Implementation of this method is left as an exercise to the readers).
The limitation of the above method discussed above is huge memory consumption when frequency of occurrence is high. If the input is 997, 8761 and 1, this method is clearly not efficient.
How can we reduce the memory consumption? Following is detailed algorithm that uses O(n) extra space where n is number of elements in input arrays.1. Take an auxiliary array (say prefix[]) of size n. 2. Populate it with prefix sum, such that prefix[i] represents sum of numbers from 0 to i. 3. Generate a random number(say r) between 1 to Sum(including both), where Sum represents summation of input frequency array. 4. Find index of Ceil of random number generated in step #3 in the prefix array. Let the index be indexc. 5. Return the random number arr[indexc], where arr[] contains the input n numbers.  Before we go to the implementation part, let us have quick look at the algorithm with an example:       arr[]: {10, 20, 30}       freq[]: {2, 3, 1}       Prefix[]: {2, 5, 6}   Since last entry in prefix is 6, all possible values of r are [1, 2, 3, 4, 5, 6]          1: Ceil is 2. Random number generated is 10.          2: Ceil is 2. Random number generated is 10.          3: Ceil is 5. Random number generated is 20.          4: Ceil is 5. Random number generated is 20.          5: Ceil is 5. Random number generated is 20.          6. Ceil is 6. Random number generated is 30.   In the above example       10 is generated with probability 2/6.       20 is generated with probability 3/6.       30 is generated with probability 1/6.
How does this work? Any number input[i] is generated as many times as its frequency of occurrence because there exists count of integers in range(prefix[i – 1], prefix[i]] is input[i]. Like in the above example 3 is generated thrice, as there exists 3 integers 3, 4 and 5 whose ceil is 5. 
C++
C
Java
Python3
C#
Javascript
// C++ program to generate random numbers// according to given frequency distribution#include <bits/stdc++.h>using namespace std; // Utility function to find ceiling of r in arr[l..h]int findCeil(int arr[], int r, int l, int h){    int mid;    while (l < h)    {        mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2        (r > arr[mid]) ? (l = mid + 1) : (h = mid);    }    return (arr[l] >= r) ? l : -1;} // The main function that returns a random number// from arr[] according to distribution array// defined by freq[]. n is size of arrays.int myRand(int arr[], int freq[], int n){    // Create and fill prefix array    int prefix[n], i;    prefix[0] = freq[0];    for (i = 1; i < n; ++i)        prefix[i] = prefix[i - 1] + freq[i];     // prefix[n-1] is sum of all frequencies.    // Generate a random number with    // value from 1 to this sum    int r = (rand() % prefix[n - 1]) + 1;     // Find index of ceiling of r in prefix array    int indexc = findCeil(prefix, r, 0, n - 1);    return arr[indexc];} // Driver codeint main(){    int arr[] = {1, 2, 3, 4};    int freq[] = {10, 5, 20, 100};    int i, n = sizeof(arr) / sizeof(arr[0]);     // Use a different seed value for every run.    srand(time(NULL));     // Let us generate 10 random numbers according to    // given distribution    for (i = 0; i < 5; i++)    cout << myRand(arr, freq, n) << endl;     return 0;} // This is code is contributed by rathbhupendra
//C program to generate random numbers according to given frequency distribution#include <stdio.h>#include <stdlib.h> // Utility function to find ceiling of r in arr[l..h]int findCeil(int arr[], int r, int l, int h){    int mid;    while (l < h)    {         mid = l + ((h - l) >> 1);  // Same as mid = (l+h)/2        (r > arr[mid]) ? (l = mid + 1) : (h = mid);    }    return (arr[l] >= r) ? l : -1;} // The main function that returns a random number from arr[] according to// distribution array defined by freq[]. n is size of arrays.int myRand(int arr[], int freq[], int n){    // Create and fill prefix array    int prefix[n], i;    prefix[0] = freq[0];    for (i = 1; i < n; ++i)        prefix[i] = prefix[i - 1] + freq[i];     // prefix[n-1] is sum of all frequencies. Generate a random number    // with value from 1 to this sum    int r = (rand() % prefix[n - 1]) + 1;     // Find index of ceiling of r in prefix array    int indexc = findCeil(prefix, r, 0, n - 1);    return arr[indexc];} // Driver program to test above functionsint main(){    int arr[]  = {1, 2, 3, 4};    int freq[] = {10, 5, 20, 100};    int i, n = sizeof(arr) / sizeof(arr[0]);     // Use a different seed value for every run.    srand(time(NULL));     // Let us generate 10 random numbers according to    // given distribution    for (i = 0; i < 5; i++)      printf("%d\n", myRand(arr, freq, n));     return 0;}
// Java program to generate random numbers// according to given frequency distributionclass GFG{ // Utility function to find ceiling of r in arr[l..h]static int findCeil(int arr[], int r, int l, int h){    int mid;    while (l < h)    {        mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2        if(r > arr[mid])            l = mid + 1;        else            h = mid;    }    return (arr[l] >= r) ? l : -1;} // The main function that returns a random number// from arr[] according to distribution array// defined by freq[]. n is size of arrays.static int myRand(int arr[], int freq[], int n){    // Create and fill prefix array    int prefix[] = new int[n], i;    prefix[0] = freq[0];    for (i = 1; i < n; ++i)        prefix[i] = prefix[i - 1] + freq[i];     // prefix[n-1] is sum of all frequencies.    // Generate a random number with    // value from 1 to this sum    int r = ((int)(Math.random()*(323567)) % prefix[n - 1]) + 1;     // Find index of ceiling of r in prefix array    int indexc = findCeil(prefix, r, 0, n - 1);    return arr[indexc];} // Driver codepublic static void main(String args[]){    int arr[] = {1, 2, 3, 4};    int freq[] = {10, 5, 20, 100};    int i, n = arr.length;     // Let us generate 10 random numbers according to    // given distribution    for (i = 0; i < 5; i++)    System.out.println( myRand(arr, freq, n) );}} // This code is contributed by Arnab Kundu
# Python3 program to generate random numbers# according to given frequency distributionimport random # Utility function to find ceiling of r in arr[l..h]def findCeil(arr, r, l, h) :     while (l < h) :           mid = l + ((h - l) >> 1); # Same as mid = (l+h)/2        if r > arr[mid] :            l = mid + 1        else :            h = mid         if arr[l] >= r :        return l    else :        return -1 # The main function that returns a random number# from arr[] according to distribution array# defined by freq[]. n is size of arrays.def myRand(arr, freq, n) :     # Create and fill prefix array    prefix = [0] * n    prefix[0] = freq[0]    for i in range(n) :        prefix[i] = prefix[i - 1] + freq[i]     # prefix[n-1] is sum of all frequencies.    # Generate a random number with    # value from 1 to this sum    r = random.randint(0, prefix[n - 1]) + 1     # Find index of ceiling of r in prefix array    indexc = findCeil(prefix, r, 0, n - 1)    return arr[indexc] # Driver codearr = [1, 2, 3, 4]freq = [10, 5, 20, 100]n = len(arr) # Let us generate 10 random numbers according to# given distributionfor i in range(5) :    print(myRand(arr, freq, n))     # This code is contributed by divyesh072019
// C# program to generate random numbers// according to given frequency distributionusing System; class GFG{     // Utility function to find ceiling// of r in arr[l..h] static int findCeil(int[] arr, int r,                    int l, int h) {     int mid;    while (l < h)     {                  // Same as mid = (l+h)/2         mid = l + ((h - l) >> 1);                 if (r > arr[mid])             l = mid + 1;        else            h = mid;     }     return (arr[l] >= r) ? l : -1; } // The main function that returns a random number// from arr[] according to distribution array // defined by freq[]. n is size of arrays. static int myRand(int[] arr, int[] freq, int n) {          // Create and fill prefix array     int[] prefix = new int[n];    int i;     prefix[0] = freq[0];          for(i = 1; i < n; ++i)         prefix[i] = prefix[i - 1] + freq[i];        // prefix[n-1] is sum of all frequencies.    // Generate a random number with     // value from 1 to this sum    Random rand = new Random();    int r = ((int)(rand.Next() * (323567)) %                      prefix[n - 1]) + 1;        // Find index of ceiling of r in prefix array     int indexc = findCeil(prefix, r, 0, n - 1);     return arr[indexc]; } // Driver Codestatic void Main(){    int[] arr = { 1, 2, 3, 4 };     int[] freq = { 10, 5, 20, 100 };     int i, n = arr.Length;          // Let us generate 10 random numbers    // according to given distribution     for(i = 0; i < 5; i++)         Console.WriteLine(myRand(arr, freq, n)); }} // This code is contributed by divyeshrabadiya07
<script> // JavaScript program to generate random numbers// according to given frequency distribution     // Utility function to find ceiling of r in arr[l..h]    function findCeil(arr, r, l, h)    {        let mid;        while (l < h)        {            mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2            (r > arr[mid]) ? (l = mid + 1) : (h = mid);        }        return (arr[l] >= r) ? l : -1;    }     // The main function that returns a random number    // from arr[] according to distribution array    // defined by freq[]. n is size of arrays.    function myRand(arr, freq,  n) {        // Create and fill prefix array        let prefix= [];        let i;        prefix[0] = freq[0];        for (i = 1; i < n; ++i)            prefix[i] = prefix[i - 1] + freq[i];         // prefix[n-1] is sum of all frequencies.        // Generate a random number with        // value from 1 to this sum        let r = Math.floor((Math.random()* prefix[n - 1])) + 1;         // Find index of ceiling of r in prefix array        let indexc = findCeil(prefix, r, 0, n - 1);        return arr[indexc];    }     // Driver code    let arr = [1, 2, 3, 4];    let freq = [10, 5, 20, 100];    let i;    let n = arr.length;     // Use a different seed value for every run.     // Let us generate 10 random numbers according to    // given distribution    for (i = 0; i < 5; i++)      document.write(myRand(arr, freq, n));             // This code is contributed by rohitsingh07052.</script>
Output: May be different for different runs 
4
3
4
4
4
Time Complexity: O(n)
This article is compiled by Aashish Barnwal. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above. 
rathbhupendra
pankaj182
andrew1234
divyeshrabadiya07
divyesh072019
rohitsingh07052
abhishek0719kadiyan
surindertarika1234
subhammahato348
prefix-sum
Mathematical
Randomized
prefix-sum
Mathematical
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Merge two sorted arrays
Operators in C / C++
Sieve of Eratosthenes
Prime Numbers
Program to find GCD or HCF of two numbers
K'th Smallest/Largest Element in Unsorted Array | Set 2 (Expected Linear Time)
Shuffle a given array using Fisher–Yates shuffle Algorithm
QuickSort using Random Pivoting
Shuffle or Randomize a list in Java
Generating Random String Using PHP