kdelibs/kdecore/util/krandomsequence.cpp
2014-11-13 01:04:59 +02:00

220 lines
5.3 KiB
C++

/*
This file is part of the KDE libraries
Copyright (c) 1999 Sean Harmer <sh@astro.keele.ac.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "krandomsequence.h"
#include "krandom.h"
static const int s_nShuffleTableSize = 32;
class KRandomSequence::Private
{
public:
// Generate the random number
void draw();
long lngSeed1;
long lngSeed2;
long lngShufflePos;
long shuffleArray[s_nShuffleTableSize];
};
//////////////////////////////////////////////////////////////////////////////
// Construction / Destruction
//////////////////////////////////////////////////////////////////////////////
KRandomSequence::KRandomSequence( long lngSeed1 ) : d(new Private)
{
// Seed the generator
setSeed( lngSeed1 );
}
KRandomSequence::~KRandomSequence()
{
delete d;
}
KRandomSequence::KRandomSequence(const KRandomSequence &a) : d(new Private)
{
*d = *a.d;
}
KRandomSequence & KRandomSequence::operator=(const KRandomSequence &a)
{
if ( this != &a ) {
*d = *a.d;
}
return *this;
}
//////////////////////////////////////////////////////////////////////////////
// Member Functions
//////////////////////////////////////////////////////////////////////////////
void KRandomSequence::setSeed( long lngSeed1 )
{
// Convert the positive seed number to a negative one so that the draw()
// function can intialise itself the first time it is called. We just have
// to make sure that the seed used != 0 as zero perpetuates itself in a
// sequence of random numbers.
if ( lngSeed1 < 0 )
{
d->lngSeed1 = -1;
}
else if (lngSeed1 == 0)
{
d->lngSeed1 = -((KRandom::random() & ~1)+1);
}
else
{
d->lngSeed1 = -lngSeed1;
}
}
static const long sMod1 = 2147483563;
static const long sMod2 = 2147483399;
void KRandomSequence::Private::draw()
{
static const long sMM1 = sMod1 - 1;
static const long sA1 = 40014;
static const long sA2 = 40692;
static const long sQ1 = 53668;
static const long sQ2 = 52774;
static const long sR1 = 12211;
static const long sR2 = 3791;
static const long sDiv = 1 + sMM1 / s_nShuffleTableSize;
// Long period (>2 * 10^18) random number generator of L'Ecuyer with
// Bayes-Durham shuffle and added safeguards. Returns a uniform random
// deviate between 0.0 and 1.0 (exclusive of the endpoint values). Call
// with a negative number to initialize; thereafter, do not alter idum
// between successive deviates in a sequence. RNMX should approximate
// the largest floating point value that is less than 1.
int j; // Index for the shuffle table
long k;
// Initialise
if ( lngSeed1 <= 0 )
{
lngSeed2 = lngSeed1;
// Load the shuffle table after 8 warm-ups
for ( j = s_nShuffleTableSize + 7; j >= 0; --j )
{
k = lngSeed1 / sQ1;
lngSeed1 = sA1 * ( lngSeed1 - k*sQ1) - k*sR1;
if ( lngSeed1 < 0 )
{
lngSeed1 += sMod1;
}
if ( j < s_nShuffleTableSize )
{
shuffleArray[j] = lngSeed1;
}
}
lngShufflePos = shuffleArray[0];
}
// Start here when not initializing
// Compute lngSeed1 = ( lngIA1*lngSeed1 ) % lngIM1 without overflows
// by Schrage's method
k = lngSeed1 / sQ1;
lngSeed1 = sA1 * ( lngSeed1 - k*sQ1 ) - k*sR1;
if ( lngSeed1 < 0 )
{
lngSeed1 += sMod1;
}
// Compute lngSeed2 = ( lngIA2*lngSeed2 ) % lngIM2 without overflows
// by Schrage's method
k = lngSeed2 / sQ2;
lngSeed2 = sA2 * ( lngSeed2 - k*sQ2 ) - k*sR2;
if ( lngSeed2 < 0 )
{
lngSeed2 += sMod2;
}
j = lngShufflePos / sDiv;
lngShufflePos = shuffleArray[j] - lngSeed2;
shuffleArray[j] = lngSeed1;
if ( lngShufflePos < 1 )
{
lngShufflePos += sMM1;
}
}
void
KRandomSequence::modulate(int i)
{
d->lngSeed2 -= i;
if ( d->lngSeed2 < 0 )
{
d->lngShufflePos += sMod2;
}
d->draw();
d->lngSeed1 -= i;
if ( d->lngSeed1 < 0 )
{
d->lngSeed1 += sMod1;
}
d->draw();
}
double
KRandomSequence::getDouble()
{
static const double finalAmp = 1.0 / double( sMod1 );
static const double epsilon = 1.2E-7;
static const double maxRand = 1.0 - epsilon;
double temp;
d->draw();
// Return a value that is not one of the endpoints
if ( ( temp = finalAmp * d->lngShufflePos ) > maxRand )
{
// We don't want to return 1.0
return maxRand;
}
else
{
return temp;
}
}
unsigned long
KRandomSequence::getLong(unsigned long max)
{
d->draw();
return max ? (((unsigned long) d->lngShufflePos) % max) : 0;
}
bool
KRandomSequence::getBool()
{
d->draw();
return (((unsigned long) d->lngShufflePos) & 1);
}