随机但可预测的数字生成器?[C++]
Random but predictable number generator? [C++]
我真的不知道如何搜索我要找的东西。谷歌给出了大量的结果,但没有一个符合我的标准。
所以我在这里问:有没有已知的代码可以创建一个数字,这个数字是可预测的,看起来是随机的,并且基于"种子"(在我的情况下是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 ); };
};
查看启动器的srand
和rand
。
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中做到了这一点,效果很好。)
srand
和rand
的问题在于,只有它们的调用签名(而不是它们生成的值)是由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();
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 检查输入是否不是整数或数字
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 如何用数字处理log(0)
- 最高有效数字侧的第N位
- 如何获取一个数字的前3位
- 查找最接近的大于当前数字的数字的索引
- 找到两对数字,使它们的乘积的绝对差最小化
- 我想做一个彼此不同但重复出现的数字
- 将数字转换为字母(例如:123 转换为一二三)
- C++如何计算用户输入的数字中的偶数位数
- 如何在C++中确定文本文件中的元素是字符还是数字
- 打印数字图案
- C++问题:用户认为数字1-100,程序提出问题不超过6次即可得到答案。无法正确
- 如何检查一个c++字符串中有多少相同的字符/数字
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 将数字打印成文字
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 在将数字随机生成为数组期间从内存输出随机数的数组