c - C 中的随机整数,rand()%N 与整数运算相比有多糟糕?它的缺陷是什么?

标签 c random distribution

编辑: 我的问题是:rand()%N 被认为是非常糟糕的,而使用整数运算被认为是更好的,但我看不出两者之间的区别。

人们总是提到:

  • rand()%N 中的低位不是随机的,

  • rand()%N 非常可预测,

  • 您可以将其用于游戏但不能用于密码学

谁能解释一下这里是否存在这些要点以及如何看待?

低位非随机性的想法应该使我展示的两种情况的 PE 不同,但事实并非如此。

我想很多像我这样的人总是会避免使用 rand()rand()%N,因为我们一直被教导说它很糟糕。我很想知道用 c rand()%N 生成的“错误”随机整数是如何有效的。这也是 Ryan Reich 在 How to generate a random integer number from within a range 中的回答的跟进。 .

老实说,那里的解释听起来很有说服力;尽管如此,我想我会试一试。所以,我以一种非常天真的方式比较分布。我为不同数量的样本和域运行两个随机生成器。我没有看到计算密度而不是直方图的意义,所以我只是计算了直方图,并且只是通过观察,我会说它们看起来一样均匀。关于提出的另一点,关于实际的随机性(尽管是均匀分布的)。我再次天真地计算了这些运行的排列熵,这对于两个样本集是相同的,这告诉我们两者在出现的顺序方面没有区别。

所以,对于很多目的,在我看来,rand()%N 就可以了,我们怎么能看到他们的缺陷呢?

在这里,我将向您展示一种非常简单、低效且不是很优雅(但我认为是正确的)的方法来计算这些样本并获得直方图和排列熵。 对于不同数量的样本,我在 {5,10,25,50,100} 中显示域 (0,i) 的图:

5 values, 5k samples

10 values 10k samples

25 values, 250k samples

100values,  1M samples

我猜代码中没有太多可看的内容,因此我将保留 C 和 matlab 代码以供复制。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[]){
        unsigned long max = atoi(argv[2]);
        int samples=atoi(argv[3]);
        srand(time(NULL));
        if(atoi(argv[1])==1){
                for(int i=0;i<samples;++i)
                        printf("%ld\n",rand()%(max+1));

        }else{
                for(int i=0;i<samples;++i){
                        unsigned long
                        num_bins = (unsigned long) max + 1,
                        num_rand = (unsigned long) RAND_MAX + 1,
                        bin_size = num_rand / num_bins,
                        defect   = num_rand % num_bins;

                        long x;
                        do {
                                x = rand();
                        }
                        while (num_rand - defect <= (unsigned long)x);
                        printf("%ld\n",x/bin_size);
                }
        }
        return 0;
}

下面是绘制此图并计算 PE 的 Matlab 代码(我从中获取的排列的递归:https://www.mathworks.com/matlabcentral/answers/308255-how-to-generate-all-possible-permutations-without-using-the-function-perms-randperm):

system('gcc randomTest.c -o randomTest.exe;');
max = 100;
samples = max*10000;
trials = 200;
system(['./randomTest.exe 1 ' num2str(max) ' ' num2str(samples) ' > file1'])
system(['./randomTest.exe 2 ' num2str(max) ' ' num2str(samples) ' > file2'])
a1=load('file1');
a2=load('file2');
uni = figure(1);
title(['Samples: ' num2str(samples)])
subplot(1,3,1)
h1 = histogram(a1,max+1);
title('rand%(max+1)')
subplot(1,3,2)
h2 = histogram(a2,max+1);
title('Integer arithmetic')
as=[a1,a2];
ns=3:8;
H = nan(numel(ns),size(as,2));
for op=1:size(as,2)
    x = as(:,op);
    for n=ns
        sequenceOcurrence = zeros(1,factorial(n));
        sequences = myperms(1:n);
        sequencesArrayIdx = sum(sequences.*10.^(size(sequences,2)-1:-1:0),2);
        for i=1:numel(x)-n
            [~,sequenceOrder] = sort(x(i:i+n-1));
            out = sequenceOrder'*10.^(numel(sequenceOrder)-1:-1:0).';
            sequenceOcurrence(sequencesArrayIdx == out) = sequenceOcurrence(sequencesArrayIdx == out) + 1;
        end
        chunks = length(x) - n + 1;
        ps = sequenceOcurrence/chunks;
        hh = sum(ps(logical(ps)).*log2(ps(logical(ps))));
        H(n,op) = hh/log2(factorial(n));
    end
end
subplot(1,3,3)
plot(ns,H(ns,:),'--*','linewidth',2)
ylabel('PE')
xlabel('Sequence length')
filename = ['all_' num2str(max) '_' num2str(samples) ];
export_fig(filename)

最佳答案

由于模运算的工作方式,如果 N 与 RAND_MAX 相比很重要,则执行 %N 会成功,因此您获得某些值的可能性比其他值大得多。假设 RAND_MAX 为 12,N 为 9。如果分布良好,则获得 0、1 或 2 之一的机会为 0.5,获得 3、4、5、6、7、8 之一的机会为0.5。结果是你得到 0 而不是 4 的可能性是 2 倍。如果 N 是 RAND_MAX 的精确除数,则不会发生此分布问题,如果 N 与 RAND_MAX 相比非常小,则问题变得不太明显。 RAND_MAX 可能不是一个特别大的值(可能是 2^15 - 1),使这个问题比您预期的更糟。执行 (rand() * n)/(RAND_MAX + 1) 的替代方案也不会给出均匀分布,但是,它将是每个 m 值(对于某些 m) 更可能发生,而不是更可能的值都处于分布的低端。

如果 N 是 RAND_MAX 的 75%,则分布底部三分之一中的值的可能性是顶部三分之二中值的两倍(因为这是额外值映射到的位置)

rand() 的质量将取决于您所使用的系统的实现。我相信某些系统的实现非常糟糕,OS X 的手册页声明 rand 已过时。 Debian 手册页说明如下:

Linux C 库中的 rand() 和 srand() 版本使用相同的 随机数生成器为 random(3) 和 srandom(3),因此低阶 位应该与高阶位一样随机。然而,在年长的 rand() 实现,以及不同的当前实现 系统中,低阶位的随机性远低于高阶位 订单位。不要在旨在成为应用程序的应用程序中使用此功能 当需要良好的随机性时可移植。 (改用 random(3)。)

关于c - C 中的随机整数,rand()%N 与整数运算相比有多糟糕?它的缺陷是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49880304/

相关文章:

python - 如何最好地分发具有 _large_ 数据依赖性的 python 包

statistics - 衡量两个分布之间的差异

c - 套接字编程,ipv6客户端程序不工作

c - 如果集合在进程之间不可分割,则使用 MPI_Scatter

python - 如何在 python 中创建一个加密安全的随机数?

c++ - 如何测试 std::random_device 的随机性?

mysql程序 - 从五个数字中随机数

linux - 在外部网上分发文件包

c - 如何从 C 代码发出 R 异常信号

c++ - 所有 .txt 文件的列表