任意概率分布的随机数发生器

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

null

例子:

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的辅助数组(比如前缀[])。 2. 用前缀sum填充它,这样前缀[i]表示从0到i的数字之和。 3. 生成一个介于1和Sum(包括两者)之间的随机数(比如r),其中Sum表示输入频率数组的总和。 4. 在前缀数组中查找步骤#3中生成的随机数的Ceil索引。让索引成为索引 C . 5. 返回随机数arr[indexc],其中arr[]包含输入的n个数字。 在我们进入实现部分之前,让我们通过一个例子快速了解一下算法: arr[]:{10,20,30} 频率[]:{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]的生成次数与其出现频率相同,因为范围内存在整数计数(前缀[i–1],前缀[i]]是输入[i]。与上面的示例一样,3是三次生成的,因为有三个整数3、4和5的ceil是5。

C++

// 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 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 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

//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 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 according to
// given distribution
for (i = 0; i < 5; i++)
printf ( "%d" , 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 array
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 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

# 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 array
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 according 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 array
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
// according to given distribution
for (i = 0; i < 5; i++)
Console.WriteLine(myRand(arr, freq, n));
}
}
// This code is contributed by divyeshrabadiya07


Javascript

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


输出: 不同的跑步可能会有所不同

43444

时间复杂性: O(n)

本文由 阿希什·巴恩瓦尔 。如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请发表评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享