📜  从流中选择一个具有O(1)空间的随机数

📅  最后修改于: 2021-04-23 16:28:22             🧑  作者: Mango

给定一个数字流,请从该流中生成一个随机数。您只能使用O(1)空间,并且输入采用流的形式,因此不能存储以前看到的数字。

那么我们如何从整个流中生成一个随机数,以使选择任何数字的概率为1 / n。有O(1)多余的空间?这个问题是储层采样的一种变化。此处k的值为1。

1)将“ count”初始化为0,“ count”用于存储到目前为止在流中看到的数字计数。
2)对于流中的每个数字“ x”,请执行以下操作
….. a)将“计数”加1。
….. b)如果count为1,则将结果设置为x,然后返回结果。
….. c)生成一个从0到’count-1’的随机数。令生成的随机数为i。
….. d)如果i等于“ count – 1”,则将结果更新为x。

C++
// An efficient C++ program to randomly select 
// a number from stream of numbers. 
#include 
#include 
using namespace std;
  
// A function to randomly select a item 
// from stream[0], stream[1], .. stream[i-1] 
int selectRandom(int x) 
{ 
    static int res; // The resultant random number 
    static int count = 0; // Count of numbers visited
                          // so far in stream 
  
    count++; // increment count of numbers seen so far 
  
    // If this is the first element from stream, 
    // return it 
    if (count == 1) 
        res = x; 
    else
    { 
        // Generate a random number from 0 to count - 1 
        int i = rand() % count; 
  
        // Replace the prev random number with 
        // new number with 1/count probability 
        if (i == count - 1) 
            res = x; 
    } 
    return res; 
} 
  
// Driver Code
int main() 
{ 
    int stream[] = {1, 2, 3, 4}; 
    int n = sizeof(stream) / sizeof(stream[0]); 
  
    // Use a different seed value for every run. 
    srand(time(NULL)); 
    for (int i = 0; i < n; ++i) 
        cout << "Random number from first " << i + 1 
             << " numbers is " << selectRandom(stream[i]) << endl; 
    return 0; 
} 
  
// This is code is contributed by rathbhupendra


C
// An efficient C program to randomly select a number from stream of numbers.
#include 
#include 
#include 
  
// A function to randomly select a item from stream[0], stream[1], .. stream[i-1]
int selectRandom(int x)
{
    static int res;    // The resultant random number
    static int count = 0;  //Count of numbers visited so far in stream
  
    count++;  // increment count of numbers seen so far
  
    // If this is the first element from stream, return it
    if (count == 1)
        res = x;
    else
    {
        // Generate a random number from 0 to count - 1
        int i = rand() % count;
  
        // Replace the prev random number with new number with 1/count probability
        if (i == count - 1)
            res  = x;
    }
    return res;
}
  
// Driver program to test above function.
int main()
{
    int stream[] = {1, 2, 3, 4};
    int n = sizeof(stream)/sizeof(stream[0]);
  
    // Use a different seed value for every run.
    srand(time(NULL));
    for (int i = 0; i < n; ++i)
        printf("Random number from first %d numbers is %d \n",
                                i+1, selectRandom(stream[i]));
    return 0;
}


Java
//An efficient Java program to randomly select a number from stream of numbers.
  
import java.util.Random;
  
public class GFG 
{
    static int res = 0;    // The resultant random number
    static int count = 0;  //Count of numbers visited so far in stream
      
    //A method to randomly select a item from stream[0], stream[1], .. stream[i-1]
    static int selectRandom(int x)
    {
        count++; // increment count of numbers seen so far
          
        // If this is the first element from stream, return it
        if (count == 1)
            res = x;
        else
        {
             // Generate a random number from 0 to count - 1
            Random r = new Random();
            int i = r.nextInt(count);
              
            // Replace the prev random number with new number with 1/count probability
            if(i == count - 1)
                res = x;
        }
        return res;
    }
      
    // Driver program to test above function.
    public static void main(String[] args)
    {
        int stream[] = {1, 2, 3, 4};
        int n = stream.length;
        for(int i = 0; i < n; i++)
            System.out.println("Random number from first " + (i+1) +
                               " numbers is " + selectRandom(stream[i]));
    }
}
//This code is contributed by Sumit Ghosh


Python3
# An efficient python3 program 
# to randomly select a number
# from stream of numbers.
import random
  
# A function to randomly select a item
# from stream[0], stream[1], .. stream[i-1]
def selectRandom(x):
      
    # The resultant random number
    res = 0;
      
    # Count of numbers visited 
    # so far in stream
    count = 0;
  
    # increment count of numbers 
    # seen so far
    count += 1;
  
    # If this is the first element 
    # from stream, return it
    if (count == 1):
        res = x;
    else:
          
        # Generate a random number 
        # from 0 to count - 1
        i = random.randrange(count);
  
        # Replace the prev random number 
        # with new number with 1/count 
        # probability
        if (i == count - 1):
            res = x;
    return res;
  
# Driver Code
stream = [1, 2, 3, 4];
n = len(stream);
  
# Use a different seed value 
# for every run.
for i in range (n):
    print("Random number from first", 
         (i + 1), "numbers is", 
          selectRandom(stream[i]));
  
# This code is contributed by mits


C#
// An efficient C# program to randomly 
// select a number from stream of numbers.
using System;
  
class GFG 
{
    // The resultant random number
    static int res = 0; 
      
    // Count of numbers visited
    // so far in stream
    static int count = 0; 
      
    // A method to randomly select 
    // a item from stream[0], 
    // stream[1], .. stream[i-1]
    static int selectRandom(int x)
    {
        // increment count of
        // numbers seen so far
        count++; 
          
        // If this is the first 
        // element from stream, 
        // return it
        if (count == 1)
            res = x;
        else
        {
            // Generate a random number 
            // from 0 to count - 1
            Random r = new Random();
            int i = r.Next(count);
              
            // Replace the prev random 
            // number with new number 
            // with 1/count probability
            if(i == count - 1)
                res = x;
        }
        return res;
    }
      
// Driver Code
public static void Main()
{
        int[] stream = {1, 2, 3, 4};
        int n = stream.Length;
        for(int i = 0; i < n; i++)
            Console.WriteLine("Random number from " + 
                              "first {0} numbers is {1}" ,
                          i + 1, selectRandom(stream[i]));
    }
}
  
// This code is contributed by mits


PHP


输出:

Random number from first 1 numbers is 1
Random number from first 2 numbers is 1
Random number from first 3 numbers is 3
Random number from first 4 numbers is 4

辅助空间: O(1)

这是如何运作的
我们需要证明以1 / n的概率选择了每个元素,其中n是到目前为止看到的项数。对于每个新的流项x,我们从0到’count -1’中选择一个随机数,如果选择的数字为’count-1’,则将先前的结果替换为x。

为了简化证明,让我们首先考虑最后一个元素,最后一个元素以1 / n的概率替换先前存储的结果。因此,获得最后一个元素作为结果的概率为1 / n。

现在让我们谈谈倒数第二个元素。当倒数第二个元素第一次处理时,它替换先前结果的概率为1 /(n-1)。当考虑第n个项目时,前一个结果保留的概率为(n-1)/ n。因此,在最后一次迭代中选择倒数第二个元素的概率为[1 /(n-1)] * [(n-1)/ n],即1 / n。

同样,我们可以证明倒数第三要素和其他要素。

参考:
储层采样