Random Ada随机数是相同的

Random Ada随机数是相同的,random,ada,Random,Ada,我的函数有以下问题,它应该返回一个随机数。当我想通过调用该函数来生成两个数字时,它们是完全相同的。如何解决调用函数时始终返回相同号码的问题?我需要那个随机变量来保持功能 代码如下: with Ada.Numerics.discrete_Random function generate_random_number ( n: in Positive) return Integer is subtype Rand_Range is Integer range 0 .. n;

我的函数有以下问题,它应该返回一个随机数。当我想通过调用该函数来生成两个数字时,它们是完全相同的。如何解决调用函数时始终返回相同号码的问题?我需要那个随机变量来保持功能

代码如下:

with Ada.Numerics.discrete_Random

function generate_random_number ( n: in Positive) return Integer is
       subtype Rand_Range is Integer range 0 .. n;
       package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
       use Rand_Int;
       gen : Rand_Int.Generator;
       ret_val: Rand_Range;
   begin
      Rand_Int.Reset(gen);
      ret_val := Random(gen);

      return ret_val;
   end;

只有一个参数的
Random
过程应以“时间相关状态”启动随机数生成器。但是,如果连续多次快速调用,则很可能程序每次使用的“时间”都相同,因为程序的运行速度可能快于时钟分辨率。(在两次使用
generate_random_number
之间,使用类似于
delay 0.5
的方法进行测试;很可能会得到两个不同的结果。)

在任何情况下,随机数生成器的预期用途是在其上使用一次
Reset
,以设置种子,然后生成从种子开始的随机数序列。通过这样做,您还可以每次使用相同的种子,以获得可复制的随机数序列,用于测试;也可以使用时间相关的种子。但是,每次需要随机数时重置随机数生成器不是使用RNG的正常或推荐方法。因此,您需要将
Reset
调用移出
generate\u random\u number
。不幸的是,这也意味着您必须使用相同的生成器,即使每次调用的
n
参数不同,这意味着您可能会被迫使用
Float\u Random
而不是
Discrete\u Random


p.S.进一步研究,我在G.2.5中发现:“对时间相关重置程序的两次不同调用应将生成器重置为不同的状态,前提是调用在时间上至少间隔1秒,但不超过50年。”[强调我的。]我确信当您尝试时,调用间隔不到1秒。

随机生成器
gen
不应是函数的本地。目前,您在每次
generate\u random\u number
调用时都会重新创建它,并对其进行初始化,因此您总是会得到相同的结果也就不足为奇了

例如,如果您将
gen
作为一个全局变量,初始化一次,那么每次使用它都会得到一个新的随机数。(是的,全局变量不好:但请参见下文)

不幸的是,
函数generate_random_number(n:正)
的语义不能很好地发挥作用,该函数每次都会以不同的方式限制随机数的范围。最简单的解决方案是让
gen
返回任何有效的整数,并使用模运算为每次调用返回正确范围内的数字。这将起作用,但您应该知道,它可能会引入加密弱点,超出了我的分析能力

如果是这种情况,您将需要不同的方法,例如为您需要的每个范围创建不同的随机生成器;注意以不同的方式对它们进行种子设定(重置),否则可能会再次出现加密弱点,例如不同生成器之间的相关性

在任何语言中,由于所有常见的原因,全局变量的结构都很差。因此,更好的方法是将其包装成一个包,使其成为一种资源

package RandGen is
   function generate_random_number ( n: in Positive) return Positive;
end RandGen;
这就是客户需要看到的全部内容。该软件包的实现如下所示:

with Ada.Numerics.discrete_Random;
package body RandGen is

   subtype Rand_Range is Positive;
   package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);

   gen : Rand_Int.Generator;

   function generate_random_number ( n: in Positive) return Integer is
   begin
      return Rand_Int.Random(gen) mod n;  -- or mod n+1 to include the end value
   end generate_random_number;

-- package initialisation part
begin
   Rand_Int.Reset(gen);
end RandGen;

编辑:Jacob关于拒绝超出范围的值的建议更好,但如果n远小于生成器范围,则效率低下。一种解决方案可能是创建几个生成器,让
generate_random_number
函数选择覆盖0的生成器。。N浪费最少。

假设启动程序时可以确定上限:

包装
随机
(规格):

通用包装
通用随机
(规范):

通用软件包
通用\u随机
(正文):


请参见以下问题:。我对Ada不太熟悉,但看起来您每次都在用相同的号码重新播种,尽管粗略地看一下您的代码并不能看出是如何进行的。如果您每次都返回相同的号码,这告诉我,你每次都发送相同的种子,而不是一个序列中的下一个。它总是使用相同的种子进行播种,因为当我以几秒钟的延迟调用此函数两次时,它会返回另一个随机种子。有一行应该重置生成器,但看起来不起作用。如果您的目的是生成两个或更多随机数的序列,则不希望每次都重置生成器。就一次,为什么要给函数加上限?在程序执行期间,您希望使用多少不同的上限?当我删除重置调用时,无论何时调用函数或何时执行程序,函数始终返回相同的值。当然,我可以在我的函数中生成几个数字,然后记住它,但是我需要在我的程序中指定范围内大约10^10个随机数。每次我需要来自不同范围的随机数时,这就是用一个参数保持它的函数的原因。对了,不能让生成器在每次调用过程时都重新声明。我想你必须使用
Float\u Random
并进行自己的计算--sorry.RE P.S.:正如你所说的那样。我做了一些实验,当我以“另一秒序列”执行程序时,随机数是另一个(但相同)。永远不要使用
mod
来限制随机数的间隔。拒绝过多的值!(除此之外,您的解释和建议都很好。)所有解决方案都很好,谢谢。因此,您的解决方案在大多数方面符合我的要求
with Generic_Random;
with Upper_Limit_Function;
package Random is new Generic_Random (Upper_Limit => Upper_Limit_Function);
generic
   Upper_Limit : Positive;
package Generic_Random is
   subtype Values is Natural range 0 .. Upper_Limit;
   function Value return Values;
end Generic_Random;
with Ada.Numerics.Discrete_Random;
package body Generic_Random is
   package Random is new Ada.Numerics.Discrete_Random (Values);

   Source : Random.Generator;

   function Value return Values is
   begin
      return Random.Random (Source);
   end Value;
begin
   Random.Reset (Source);
end Generic_Random;