可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Well I don't really know how to search for the thing I'm looking for.
Google gives tons of results, but none which match my criteria.
So I'm asking it here:
Is there any known piece of code that can create a number, that is predictable, looks random, and is based on a 'seed' (in my case it's the unix timestamp) and between a specified range?
I want to be able to create weather forecast in a script for a game I'm coding (but I need the C++ code which I can port, I don't think many people here are familiar with 'PAWN' [a.k.a. SMALL] scripting language? :) ).
The weather id's vary from 0 to ~100, including some deprecated ID's (so my solution would be to make a array holding valid weather ID's so we don't need to worry about those BAD_ID's, let's not make the function too complicated).
I could possibly make such formula but the problem in the past I had was that the weather was changing too fast (like every second, though I lost the code somewhere :/ ) and for now I'm really out of ideas on how I'm going to make such a formula.
Any suggestions are really appreciated too!
回答1:
Look at the C implementation of the random number generator used by VB6. It's perfect for games because it generates fairly believable random seqeuences but uses a seed and the same seed always generates the same sequence. So in game data files you can save a set of seed values that will give you known (but random-looking) sequences that you can easily reproduce.
Here's an implementation that returns values in a range:
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 ); };
};
回答2:
Look into srand
and rand
for starters.
C++11 includes many more advanced algorithms as well, but for basic needs the above two are sufficient.
To keep the numbers within a range of 0 to n, use the %
operator.
回答3:
Obviously a number cannot be both "predictable" and "random" - those are directly contradictory terms.
I'm assuming what you mean is a number that is both deterministic and semirandom.
Luckily for you, this is what pseudorandom number generators (PRNGs) produce: when they are run with a consistent seed, they give you the same output.
So I would recommend setting your seed with srandom
, then using random() % MAX_VALUE
to get a number between 0 and MAX_VALUE
. If you get a "bad value", just go again. Repeat sans reseeding for as many numbers as you like.
回答4:
If you need a slow changing value you can use a noise function, such as Perlin Noise.
回答5:
What you really want is a hash function. To limit the range you can use one of the usual tricks (the dirtiest being the remainder operator).
Specifically, you want to hash integers into integers. You can pick up such a function here. I recommend the one titled "Robert Jenkins' 32 bit integer hash function" -- always worked well for me.
You'll end up with something like:
int time_index = 3;
int weather_state = integer_hash_function(time_index) % (MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE
If you want more interesting weather behavior, you can linearly interpolate between time values. You can use Perlin noise with linear combinations of such interpolated noise at differing frequencies and intensities to make some pretty nice behavior. (I've done this with multiplayer RPGs and it works well.)
回答6:
The problem with srand
and rand
is that only their call signatures (and not the values they generate) are dictated by the C standard. If you need portable and deterministic pseudo-random numbers, you should implement it yourself. Here is a class, written in C++, which is based on one found in Numerical Recipes, and is completely portable. You may instantiate the random number stream with a seed if you'd like to. I hard-code this seed instead of using the time in case I want the same pseudo-random sequence again and again. You can also use the RandomInteger(a,b)
method to get integers on the half-open interval [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;
}
回答7:
I think you can use rand for generating random numbers. However, you can give the same value to srand like say 99 so that your numbers will be random but predictable every time.
int iSecret = 0;
/* initialize random seed: */
srand ( 99 );
/* generate secret number: */
iSecret = rand();