不使用任何外部函数生成随机数

这是我最近参加的一次访谈中提出的问题。

据我所知,两个数字之间的随机数可以生成如下

public static int rand(int low, int high) { return low + (int)(Math.random() * (high - low + 1)); } 

但是在这里我使用Math.random()来生成0到1之间的随机数,并使用它来帮助我在低和高之间生成。 有没有其他方法可以在不使用外部function的情况下直接进行?

典型的伪随机数生成器基于先前的数据计算新数字,因此理论上它们是完全确定的。 通过提供良好的种子(随机数生成算法的初始化)来保证唯一的随机性。 只要随机数不是非常安全关键(这将需要“实际”随机数),这种递归随机数发生器通常满足需要。

一旦提供种子,递归生成可以在没有任何“外部”function的情况下表达。 有几种算法可以解决这个问题。 一个很好的例子是线性同余生成器 。

伪代码实现可能如下所示:

 long a = 25214903917; // These Values for a and c are the actual values found long c = 11; // in the implementation of java.util.Random(), see link long previous = 0; void rseed(long seed) { previous = seed; } long rand() { long r = a * previous + c; // Note: typically, one chooses only a couple of bits of this value, see link previous = r; return r; } 

您仍然需要为此生成器设置一些初始值。 这可以通过执行以下操作之一来完成:

  • 使用类似当前时间的东西(在大多数非安全关键的情况下都很好,比如游戏)
  • 使用硬件噪声(适用于安全关键随机性)
  • 使用常数(适合调试,因为你总是得到相同的序列)
  • 如果你不能使用任何函数并且不想使用常量种子,并且如果你使用的语言允许这样做,你也可以使用一些未初始化的内存。 例如,在C和C ++中,定义一个新变量,不要为其赋值并使用其值来为生成器设定种子。 但请注意,这远不是一个“好种子”,只是一个黑客来满足您的要求。 切勿在实际代码中使用它。

请注意, 没有算法可以为具有相同输入的 不同运行生成不同的值,而无需访问某些外部源(如系统环境)。 每个种子播种的随机数发生器都使用一些外部源。

在这里,我建议一些评论来源可能你会发现有用:

  • 系统时间 :一天中单调性随机性差。 快速,简单。
  • 鼠标点 :随机但在独立系统上没用。
  • 原始套接字/本地网络 (数据包的信息部分):良好的随机技术和耗时 – 可以建模攻击模式以减少随机性。
  • 一些输入文本与排列 :快速,通用的方式和良好(在我看来)。
  • 键盘,磁盘驱动器和其他事件引起的中断时间:常见方式 – 如果不小心使用,则容易出错。
  • 另一种方法是馈送模拟噪声信号 :例如temp。
  • /proc 文件数据 :在Linux系统上。 我觉得你应该用这个。

    /proc/sys/kernel/random:该目录包含控制文件/dev/random操作的各种参数。

    字符特殊文件/dev/random/dev/urandom (自Linux 1.3.30以来存在)提供了内核随机数生成器的接口。

    试试这个逗号:

     $cat /dev/urandom 

     $cat /dev/random 

    您可以编写从该文件读取的文件读取function。

    阅读(也建议): 来自/ dev / urandom的rand对于登录密钥是否安全?

`

System.currentTimeMillis()是否计为外部? 你可以随时得到这个并按一些最大值计算mod:

 int rand = (int)(System.currentTimeMillis()%high)+low; 

您可以使用变量的地址或组合更多变量的地址来制作更复杂的变量…

你可以从逻辑映射x = 4x(1-x)得到接近随机性(实际上是混乱的,绝对不是统一的*),从01之间的“非理性” x

出现“随机性” 是因为浮点表示精度边缘的舍入误差。

(*)一旦你知道它存在,你可以撤消倾斜。

您可以获得当前系统时间,但这也需要大多数语言的function。

如果允许使用某些外部状态(例如,使用当前系统时间初始化的长时间),则可以在没有外部函数的情况下执行此操作。 这足以让您实现一个简单的伪随机数生成器。

在每次调用随机函数时,您将使用state创建一个新的随机值,并更新状态,以便后续调用获得不同的结果。

您可以使用常规Java算法和/或按位运算来完成此操作,因此不需要外部函数。

 public class randomNumberGenerator { int generateRandomNumber(int min, int max) { return (int) ((System.currentTimeMillis() % max) + min); } public static void main(String[] args) { randomNumberGenerator rn = new randomNumberGenerator(); int cv = 0; int min = 1, max = 4; Map hmap = new HashMap(); int count = min; while (count <= max) { cv = rn.generateRandomNumber(min, max); if ((hmap.get(cv) == null) && cv >= min && cv <= max) { System.out.print(cv + ","); hmap.put(cv, 1); count++; } } } } 

泊松随机发生器

假设我们从随机数的期望值“v”开始。 然后说一系列非负整数满足具有期望值v的泊松分布意味着在子序列上,该值的平均值(平均值)将显示为“v”。 Poisson Distribution是统计数据的一部分,详细信息可以在维基百科上找到。 但是这里使用此函数的主要优点是:1。仅生成整数值。 2.这些整数的平均值将等于我们最初提供的值。

在小数值没有意义的应用中,它很有用。 就像在1分钟到达机场的飞机数量是2.5(没有意义),但它暗示在2分钟内5个计划到达。

 int poissonRandom(double expectedValue) { int n = 0; //counter of iteration double limit; double x; //pseudo random number limit = exp(-expectedValue); x = rand() / INT_MAX; while (x > limit) { n++; x *= rand() / INT_MAX; } return n; } 

这条线

 rand() / INT_MAX 

应该生成0到1之间的随机数。所以我们可以使用系统的时间。 秒/ 60将达到目的。 我们应该使用哪种function完全取决于应用程序。