随机但可预测的数字生成器?[C++]

Random but predictable number generator? [C++]

本文关键字:C++ 数字 随机      更新时间:2023-10-16

我真的不知道如何搜索我要找的东西。谷歌给出了大量的结果,但没有一个符合我的标准。

所以我在这里问:有没有已知的代码可以创建一个数字,这个数字是可预测的,看起来是随机的,并且基于"种子"(在我的情况下是unix时间戳),并且在指定的范围内?

我想能够在我正在编写的游戏的脚本中创建天气预报(但我需要可以移植的C++代码,我想这里的很多人都不熟悉"PAWN"[又名SMALL]脚本语言?:))。天气id从0到100不等,包括一些不推荐使用的id(所以我的解决方案是制作一个包含有效天气id的数组,这样我们就不必担心那些BAD_id,我们不要让函数太复杂)。

我可能会制作这样的公式,但过去的问题是天气变化太快(就像每一秒一样,尽管我在某个地方丢失了代码:/),现在我真的不知道该如何制作这样一个公式。

任何建议都非常感谢!

查看VB6使用的随机数生成器的C实现。它非常适合游戏,因为它生成了相当可信的随机序列,但使用了一个种子,并且相同的种子总是生成相同的序列。因此,在游戏数据文件中,您可以保存一组种子值,这些种子值将为您提供已知(但看起来随机)的序列,您可以轻松复制这些序列。

这里有一个返回范围内值的实现:

typedef int Int32;
typedef unsigned int UInt32;
class CRnd
{
    private:
        static const UInt32 INITIAL_VALUE = 0x50000;
        static const UInt32 INCREMENT = 0xC39EC3;
        static const UInt32 MULTIPLIER = 0x43FD43FD;
    private:
        UInt32 m_nRnd;
    public:
        CRnd () { m_nRnd = INITIAL_VALUE; };
        CRnd ( IN UInt32 nSeed ) { m_nRnd = nSeed; };
        virtual ~CRnd () {};
        Int32 Get ( IN Int32 nFrom, IN Int32 nTo )
        {
            if ( nTo < nFrom ) // nFrom should be less than nTo
            {
                Int32 nTmp = nTo;
                nTo = nFrom;
                nFrom = nTmp;
            }
            else if ( nTo == nFrom )
            {
                return ( nTo );
            }
            m_nRnd = ( m_nRnd * MULTIPLIER + INCREMENT ) & 0xFFFFFF;
            float fTmp = (float) m_nRnd / (float) 16777216.0;
            return ( (Int32) ( ( fTmp * ( nTo - nFrom + 1 ) ) + nFrom ) );
        };
        void SetSeed ( IN UInt32 nSeed ) { m_nRnd = nSeed; };
        UInt32 GetSeed () { return ( m_nRnd ); };
};

查看启动器的srandrand

C++11还包括许多更高级的算法,但对于基本需求,以上两种算法就足够了。

要将数字保持在0到n的范围内,请使用%运算符。

显然,一个数字不可能既是"可预测的"又是"随机的"——这是直接矛盾的术语。

我假设你的意思是一个既有确定性又有半随机性的数字。

幸运的是,这就是伪随机数生成器(PRNG)产生的结果:当它们使用一致的种子运行时,它们会为您提供相同的输出。

因此,我建议使用srandom设置种子,然后使用random() % MAX_VALUE获得0和MAX_VALUE之间的数字。如果你得到了一个"坏值",就再去一次。对任意数量的数字重复无补种操作。

如果需要一个缓慢变化的值,可以使用噪波函数,例如Perlin噪波。

您真正想要的是一个散列函数。为了限制范围,你可以使用一个常用的技巧(最脏的是余数运算符)。

具体来说,您希望将整数散列为整数。你可以在这里找到这样一个函数。我推荐一个名为"RobertJenkins的32位整数散列函数"的函数,它对我来说一直很好

你最终会得到这样的东西:

int time_index = 3;
int weather_state = integer_hash_function(time_index) % (MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE

如果想要更有趣的天气行为,可以在时间值之间进行线性插值。您可以将Perlin噪波与不同频率和强度的插值噪波的线性组合一起使用,以产生一些非常好的行为。(我已经在多人RPG中做到了这一点,效果很好。)

srandrand的问题在于,只有它们的调用签名(而不是它们生成的值)是由C标准规定的。如果您需要可移植的确定性伪随机数,您应该自己实现它。这是一个用C++编写的类,它基于NumericalRecipes中的一个类,并且是完全可移植的。如果你愿意,你可以用一个种子实例化随机数流。我硬编码这个种子,而不是使用时间,以防我一次又一次地想要相同的伪随机序列。您也可以使用RandomInteger(a,b)方法来获得半开区间[a,b)上的整数

class RandomNumberStream
{
private:
  unsigned long long u,v,w;
public:
  RandomNumberStream(int n=1);
  double RandomDouble();
  double RandomDouble(double a, double b);
  unsigned long long RandomInteger();
  unsigned long long RandomInteger(int a, int b);
private:
  unsigned long long int64();
} ;

RandomNumberStream::RandomNumberStream(int n)
{
  v = 4101842887655102017LL;
  w = 1;
  u = n^v; int64();
  v =   u; int64();
  w =   v; int64();
}
double RandomNumberStream::RandomDouble()
{
  return int64() * 5.42101086242752217E-20f;
}
double RandomNumberStream::RandomDouble(double a, double b)
{
  return int64() * 5.42101086242752217E-20f * (b-a) + a;
}
unsigned long long RandomNumberStream::RandomInteger()
{
  return int64();
}
unsigned long long RandomNumberStream::RandomInteger(int a, int b)
{
  return a + int64() % (b-a);
}
unsigned long long RandomNumberStream::int64()
{
  u  = u * 2862933555777941757LL + 7046029254386353087LL;
  v ^= v>>17; v ^= v<<31; v ^= v>>8;
  w  = 4294957665U*(w & 0xffffffff) + (w>>32);
  unsigned long long x = u^(u<<21); x ^= x>>35; x ^= x<<4;
  return (x+v)^w;
}

我认为可以使用rand生成随机数。然而,你可以给sra同样的值,比如99,这样你的数字每次都是随机的,但可以预测。

int iSecret = 0;
/* initialize random seed: */
srand ( 99 );
/* generate secret number: */
iSecret = rand();