Isn't it useful to show example of LCG using different prime number? Isn't it hard to read beginning, "Speaking of LCG,"
In rather unlikely case that he uses random numbers in such way that one of these methods looks visually non-random (he needs it to make 3D structures), other could quite likely look random (and vice-versa)
Pseudo-random Number Generation
I just quickly coded my own LCG random number generator class with portability in mind:
random.h:
random.cpp:
Test code:
Test output:
Float: 0.939825
Average: 0.500492
Double: 0.721694
Average: 0.499735
It seems to be working right. It generates floats between 0 and 1, and their average converges to 0.5 because they are uniformly distributed. If you are wondering about the weird hexadecimal stuff, its because I can't directly write 64 bit integers in GCC. Hence I have to write it out in two 32 bit parts, multiply one of them by 2^32 and take their sum to obtain the whole number. I did not use bit shifting because its meaning could be different on other (big endian) platforms.
random.h:
typedef unsigned long long int uint64;typedef unsigned long int uint32;typedef double float64;typedef float float32;class CRandomGenerator{public: // Constructor and destructor CRandomGenerator() {} ~CRandomGenerator() {} // Method to set a 32bit random seed void SetSeed(uint32 Seed); // Method to compute a 64bit random integer uint64 RandomInt64(); // Method to compute a 32bit random integer uint32 RandomInt32(); // Method to compute a 64bit random float float64 RandomFloat64(); // Method to compute a 32bit random float float32 RandomFloat32();private: // Constants for 64 bit random values // Xn+1 = A * Xn + B (mod 2^64) static const uint64 A_CONSTANT_64 = (uint64)(0x27BB2EE6)*(0x010000)*(0x010000) + (uint64)(0x87B0B0F); static const uint64 B_CONSTANT_64 = 0xB504F32D; // Constants for 32 bit random values // Xn+1 = A * Xn + B (mod 2^32) static const uint32 A_CONSTANT_32 = 0x0019660D; static const uint32 B_CONSTANT_32 = 0x3C6EF35F; // Maximum integer values static const uint64 MAX_INT_64 = (uint64)(0xFFFFFFFF)*(0x010000)*(0x010000) + (uint64)(0xFFFFFFFF); static const uint32 MAX_INT_32 = (uint32)(0xFFFFFFFF); // Scaling values for random float generations static const float64 FLOAT_SCALE_64 = (float64)1.0 / (float64)MAX_INT_64; static const float32 FLOAT_SCALE_32 = (float32)1.0 / (float32)MAX_INT_32; // Random seed union Seed { uint64 Int64; uint32 Int32; } m_Seed;};
random.cpp:
#include "random.h"void CRandomGenerator::SetSeed(uint32 Seed){ m_Seed.Int64 = 0; m_Seed.Int32 = Seed;}uint64 CRandomGenerator::RandomInt64(){ m_Seed.Int64 = m_Seed.Int64 * A_CONSTANT_64 + B_CONSTANT_64; return m_Seed.Int64;}uint32 CRandomGenerator::RandomInt32(){ m_Seed.Int32 = m_Seed.Int32 * A_CONSTANT_32 + B_CONSTANT_32; return m_Seed.Int32;}float64 CRandomGenerator::RandomFloat64(){ return (float64)RandomInt64() * FLOAT_SCALE_64;}float32 CRandomGenerator::RandomFloat32(){ return (float32)RandomInt32() * FLOAT_SCALE_32;}
Test code:
#include <iostream>#include "random.h"int main(int argc, char** argv){ CRandomGenerator RandomGenerator; RandomGenerator.SetSeed(1337); uint32 NumValues = 1000000; float32 Sum = 0.0f; for (uint32 i = 0; i < NumValues; ++i) { Sum += RandomGenerator.RandomFloat32(); } std::cout << "Float: " << RandomGenerator.RandomFloat32() << std::endl; std::cout << "Average: " << Sum / NumValues << std::endl; float64 Sum64 = 0.0; for (uint32 i = 0; i < NumValues; ++i) { Sum64 += RandomGenerator.RandomFloat64(); } std::cout << "Double: " << RandomGenerator.RandomFloat64() << std::endl; std::cout << "Average: " << Sum64 / NumValues << std::endl; return 0;}
Test output:
Float: 0.939825
Average: 0.500492
Double: 0.721694
Average: 0.499735
It seems to be working right. It generates floats between 0 and 1, and their average converges to 0.5 because they are uniformly distributed. If you are wondering about the weird hexadecimal stuff, its because I can't directly write 64 bit integers in GCC. Hence I have to write it out in two 32 bit parts, multiply one of them by 2^32 and take their sum to obtain the whole number. I did not use bit shifting because its meaning could be different on other (big endian) platforms.
You have to be a bit careful with using LCGs for graphics. I've seen pretty visible artifacts in procedural textures where the non-random distributions produced by many simple PRNGs start showing up. The human visual system is very sensitive to certain kinds of patterns and many PRNGs have a tendency to produce quite non-uniform distributions in 2 or 3 dimensions. If everything looks fine then stick with what works but if you start seeing peculiar artifacts you might want to try varying the generator you use.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement