给定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