Subversion Repositories public iLand

Rev

Rev 1221 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/********************************************************************************************
**    iLand - an individual based forest landscape and disturbance model
**    http://iland.boku.ac.at
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
**
**    This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
**
**    This program 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 General Public License for more details.
**
**    You should have received a copy of the GNU General Public License
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
********************************************************************************************/


#include "random.h"
#include "global.h"
#include "expression.h"
#include "exception.h"


/** @class RandomIndex Access each index of a given size in a random order.
  Example-Usage:
  @code
  RandomIndex r(100); // create
  while (r.next())
     qDebug() << r.index(); // prints out 100 numbers (0..99) in a random order.
  @endcode
*/

RandomIndex::RandomIndex(int aCount)
{
    mField=0;
    mCount=aCount;
    if (mCount>0) {
        mField=new char[mCount];
        for (int i=0;i<mCount;i++)
            mField[i]='a';
    }
    mIndex=-1;
    mRemaining=mCount;
}

RandomIndex::~RandomIndex()
{
    if (mField)
        delete []mField;
}

bool RandomIndex::next()
{
    if (mRemaining==0) {
        mIndex=-1;
        return false;
    }
    mRemaining--;
    int random_index= irandom(0, mRemaining+1); //RandomRange(0,mRemaining+1);
    int found=0;
    for (int i=0;i<mCount;i++) {
        if (mField[i]=='a') {
            if (random_index==found) {
                mIndex=i;
                mField[i]='b';
                return true;
            }
            found++;
        }

    }
    return false;
}

/** @class RandomWeighted
  */

RandomWeighted::RandomWeighted()
{
    mSize=10;
    mMemorySize=mSize;
    mGrid=new int[mMemorySize];
}

void RandomWeighted::setup(const int gridSize)
{
    if (gridSize>mMemorySize) {
        // extend memory
        delete[] mGrid;
        mMemorySize=gridSize;
        mGrid=new int[mMemorySize];
    }

    mSize=gridSize;
    for (int i=0;i<mSize;i++)
        mGrid[i]=0;
    mMaxVal=0;
    mUpdated=false;
}

RandomWeighted::~RandomWeighted()
{
    if (mGrid)
        delete[] mGrid;
}

void RandomWeighted::setWeight(const int index, const int value)
{
    if(!mGrid || index<0 || index>=mSize)
        return;
    mGrid[index]=value;
    mUpdated=false;
}

int RandomWeighted::get()
{
    if (!mGrid)
        return -1;
    if (!mUpdated)
        updateValues();
    int rnd=irandom(0, mMaxVal);
    int index=0;
    while (rnd>=mGrid[index] && index<mSize)
        index++;
    return index;

}

double RandomWeighted::getRelWeight(const int index)
{
    // das relative gewicht der Zelle "Index".
    // das ist das Delta zu Index-1 relativ zu "MaxVal".
    if (index<0 || index>=mSize)
        return 0.;
    if (!mUpdated)
        updateValues();

    if (!mMaxVal)
        return 0;

    if (index==0)
        return mGrid[0]/double(mMaxVal);

    return (mGrid[index]-mGrid[index-1]) / double(mMaxVal);
}

double RandomWeighted::getRelWeight(const int from, const int to)
{
    // das relative gewicht der Zelle "Index".
    // das ist das Delta zu Index-1 relativ zu "MaxVal".
    if (from==to)
        return getRelWeight(from);
    if (from<0 || from>=mSize || to<0 || to>=mSize || from>to)
        return 0.;
    if (!mUpdated)
        updateValues();

    if (!mMaxVal)
        return 0.;
    return (mGrid[to]-mGrid[from]) / double(mMaxVal);
}

void RandomWeighted::updateValues()
{
    int i;
    mMaxVal=0;
    for (i=0;i<mSize;i++) {
        if (mGrid[i]!=0) {
            mMaxVal+=mGrid[i];
            if (mMaxVal < 0)
                throw IException("Error: RandomWeighted::updateValues: integer overflow.");
        }
        mGrid[i]=mMaxVal;
    }
    mUpdated=true;
}


/** @class RandomCustomPDF provide random numbers with a user defined probaility density function.
    Call setup() or use the constructor to provide a function-expression with one variable (e.g. 'x^2').
    Call get() to retrieve a random number that follows the given probabilty density function. The provided function
    is not bound to a specific value range, but should produce values below 40.
  */


RandomCustomPDF::RandomCustomPDF()
{
    mExpression=0;
}
RandomCustomPDF::~RandomCustomPDF()
{
    if (mExpression)
        delete mExpression;
}
#define BIGINTVAL 100000000
/** setup of the properites of the RandomCustomPDF.
@p funcExpr the probability density function is given as a string for an Expression. The variable (e.g. 'x')
@p lowerBound lowest possible value of the random numbers (default=0)
@p upperBound highest possible value of the random numbers (default=1)
@p isSumFunc if true, the function given in 'funcExpr' is a cumulative probabilty density function (default=false)
@p stepCount internal degree of 'slots' - the more slots, the more accurate (default=100)
  */

void RandomCustomPDF::setup(const QString &funcExpr,
                            const double lowerBound,
                            const double upperBound,
                            const bool isSumFunc,
                            const int stepCount)
{
    mFunction = funcExpr;
    mSteps=stepCount;
    mSumFunction = isSumFunc;
    if (mExpression)
        delete mExpression;
    mExpression = new Expression(funcExpr);

    mRandomIndex.setup(mSteps);
    mLowerBound=lowerBound;
    mUpperBound=upperBound;
    mDeltaX = (mUpperBound-mLowerBound)/mSteps;
    double x1, x2;
    double p1, p2;
    double areaval;
    double step_width = 1. / mSteps;
    for (int i=0;i<mSteps;i++) {
        x1=mLowerBound + i*mDeltaX;
        x2=x1 + mDeltaX;
        // p1, p2: werte der pdf bei unterer und oberer grenze des aktuellen schrittes
        p1=mExpression->calculate(x1);
        p2=mExpression->calculate(x2);
        // areaval: numerische integration zwischen x1 und x2
        areaval = (p1 + p2)/2 * step_width;
        if (isSumFunc)
            areaval=areaval - p1 * step_width; // summenwahrscheinlichkeit: nur das Delta zaehlt.
        // tsetWeightghted operiert mit integers -> umrechnung: * huge_val
        mRandomIndex.setWeight(i, int(areaval*BIGINTVAL));
    }
}

double RandomCustomPDF::get()
{
    // zufallszahl ziehen.
    if (!mExpression)
        throw IException("TRandomCustomPDF: get() without setup()!"); // not set up properly
    // (1) select slot randomly:
    int slot = mRandomIndex.get();
    // the current slot is:
    double basevalue = mLowerBound + slot*mDeltaX;
    // (2): draw a uniform random number within the slot
    double value = nrandom(basevalue, basevalue + mDeltaX);
    return value;
}

double RandomCustomPDF::getProbOfRange(const double lowerBound, const double upperBound)
{
    if (mSumFunction) {
        double p1,p2;
        p1 = mExpression->calculate(lowerBound);
        p2 = mExpression->calculate(upperBound);
        return p2-p1;
    }
    // Wahrscheinlichkeit, dass wert zwischen lower- und upper-bound liegt.
    if (lowerBound>upperBound)
        return 0.;
    if (lowerBound < mLowerBound || upperBound>mUpperBound)
        return 0.;
    // "steps" is the resolution between lower and upper bound
    int iLow, iHigh;
    iLow = int( (mUpperBound- mLowerBound)/double(mSteps)*(lowerBound - mLowerBound) );
    iHigh = int( (mUpperBound -mLowerBound)/double(mSteps)*(upperBound - mUpperBound) );
    if (iLow<0 || iLow>=mSteps || iHigh<0 || iHigh>=mSteps)
        return -1;
    return mRandomIndex.getRelWeight(iLow, iHigh);

}