📜  随机数发生器以任意概率分布方式

📅  最后修改于: 2021-05-06 07:44:22             🧑  作者: Mango

给定n个数字,每个数字都有一定的出现频率。返回一个随机数,其概率与其发生频率成正比。

例子:

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

很明显,简单的随机数生成器在这里不起作用,因为它无法跟踪发生的频率。

我们需要以某种方式将问题转化为我们已知的解决方案。

一种简单的方法是获取一个辅助数组(例如aux []),然后根据数字的出现频率复制它们。生成一个介于0到Sum-1(包括两者)之间的随机数(例如r),其中Sum代表频率数组的总和(在上面的示例中为freq [])。返回随机数aux [r](此方法的实现留给读者练习)。

当出现频率高时,上面讨论的上述方法的局限性是巨大的存储器消耗。如果输入为997、8761和1,则此方法显然无效。

我们如何减少内存消耗?以下是使用O(n)额外空间的详细算法,其中n是输入数组中元素的数量。
1.取大小为n的辅助数组(例如prefix [])。
2.用前缀和填充它,以便前缀[i]表示从0到i的数字之和。
3.生成一个介于1到Sum(包括两者)之间的随机数(例如r),其中Sum表示输入频率阵列的总和。
4.在前缀数组中找到在步骤3中生成的随机数Ceil的索引。令索引为索引c
5.返回随机数arr [indexc],其中arr []包含输入的n个数字。
在转到实现部分之前,让我们通过一个示例快速了解一下该算法:
arr []:{10,20,30}
freq []:{2,3,1}
前缀[]:{2,5,6}
由于前缀中的最后一个条目为6,因此r的所有可能值为[1、2、3、4、5、6]
1:Ceil是2。生成的随机数是10。
2:Ceil是2。生成的随机数是10。
3:Ceil是5。生成的随机数是20。
4:Ceil是5。生成的随机数是20。
5:Ceil是5。生成的随机数是20。
6. Ceil是6.生成的随机数是30。
在上面的例子中
10的生成概率为2/6。
20以3/6的概率生成。
30以1/6的概率生成。

这是如何运作的?
因为存在范围内的整数计数(前缀[i – 1],前缀[i]]被输入[i],所以任何数字input [i]的生成次数都与它的出现频率一样多。就像上面的示例3一样,生成三次,因为存在3个整数,ceil为5的3、4和5。

C++
// C++ program to generate random numbers
// according to given frequency distribution
#include 
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 arrat
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver code
int 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 accroding 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
//C program to generate random numbers according to given frequency distribution
#include 
#include 
 
// 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 arrat
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver program to test above functions
int 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 accroding to
    // given distribution
    for (i = 0; i < 5; i++)
      printf("%d\n", myRand(arr, freq, n));
 
    return 0;
}


Java
// Java program to generate random numbers
// according to given frequency distribution
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)
    {
        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 arrat
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver code
public 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 accroding to
    // given distribution
    for (i = 0; i < 5; i++)
    System.out.println( myRand(arr, freq, n) );
}
}
 
// This code is contributed by Arnab Kundu


Python3
# Python3 program to generate random numbers
# according to given frequency distribution
import 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 arrat
    indexc = findCeil(prefix, r, 0, n - 1)
    return arr[indexc]
 
# Driver code
arr = [1, 2, 3, 4]
freq = [10, 5, 20, 100]
n = len(arr)
 
# Let us generate 10 random numbers accroding to
# given distribution
for i in range(5) :
    print(myRand(arr, freq, n))
 
    # This code is contributed by divyesh072019


C#
// C# program to generate random numbers
// according to given frequency distribution
using 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 arrat 
    int indexc = findCeil(prefix, r, 0, n - 1); 
    return arr[indexc]; 
}
 
// Driver Code
static 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
    // accroding to given distribution 
    for(i = 0; i < 5; i++) 
        Console.WriteLine(myRand(arr, freq, n)); 
}
}
 
// This code is contributed by divyeshrabadiya07


Javascript


输出:对于不同的运行可能有所不同

4
3
4
4
4