/*
* RapidMiner
*
* Copyright (C) 2001-2008 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools;
import java.util.Date;
import java.util.Random;
import com.rapidminer.Process;
import com.rapidminer.operator.ProcessRootOperator;
import com.rapidminer.parameter.UndefinedParameterError;
/**
* The global random number generator. This should be used for all random
* purposes of RapidMiner to ensure that two runs of the same process setup provide the
* same results.
*
* @author Ralf Klinkenberg, Ingo Mierswa
* @version $Id: RandomGenerator.java,v 1.9 2008/07/25 15:30:31 ingomierswa Exp $
*/
public class RandomGenerator extends Random {
private static final long serialVersionUID = 7562534107359981433L;
/** Use this alphabet for random String creation. */
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/**
* Global random number generator using the random number generator seed
* specified for the root operator (ProcessRootOperator).
*/
private static RandomGenerator globalRandomGenerator = new RandomGenerator(2001);
/** Initializes the random number generator without a seed. */
private RandomGenerator() {
super();
}
/** Initializes the random number generator with the given <code>seed</code> */
public RandomGenerator(long seed) {
super(seed);
}
// ================================================================================
/** Returns the global random number generator. */
public static RandomGenerator getGlobalRandomGenerator() {
return getRandomGenerator(null, -1);
}
/** Returns the global random number generator if the seed is negative and a new RandomGenerator
* with the given seed if the seed is positive or zero. This way is is possible to allow for local
* random seeds. Operators like learners or validation operators should definitely make use of such
* a local random generator. */
public static RandomGenerator getRandomGenerator(int seed) {
return getRandomGenerator(null, seed);
}
/** Returns the global random number generator if the seed is negative and a new RandomGenerator
* with the given seed if the seed is positive or zero. This way is is possible to allow for local
* random seeds. Operators like learners or validation operators should definitely make use of such
* a local random generator. */
public static RandomGenerator getRandomGenerator(Process process, int seed) {
if (seed < 0) {
if (globalRandomGenerator == null) { // might happen
init(process);
}
return globalRandomGenerator;
} else {
return new RandomGenerator(seed);
}
}
/**
* Instantiates the global random number generator and initializes it with
* the random number generator seed specified in the <code>global</code>
* section of the configuration file. Should be invoked before the
* process starts.
*/
public static void init(Process process) {
long seed = 2001;
if (process != null) {
try {
seed = process.getRootOperator().getParameterAsInt(ProcessRootOperator.PARAMETER_RANDOM_SEED);
} catch (UndefinedParameterError e) {
// tries to read the general random seed
// if no seed was specified (cannot happen) use seed 2001
seed = 2001;
}
}
if (seed == -1) // could be from process parameter
globalRandomGenerator = new RandomGenerator();
else
globalRandomGenerator = new RandomGenerator(seed);
}
// ================================================================================
/**
* Returns the next pseudorandom, uniformly distributed <code>double</code>
* value between <code>lowerBound</code> and <code>upperBound</code>
* from this random number generator's sequence (exclusive of the interval
* endpoint values).
*/
public double nextDoubleInRange(double lowerBound, double upperBound) {
if (upperBound <= lowerBound) {
throw new IllegalArgumentException("RandomGenerator.nextDoubleInRange : the upper bound of the " + "random number range should be greater than the lower bound.");
}
return ((nextDouble() * (upperBound - lowerBound)) + lowerBound);
}
/**
* returns the next pseudorandom, uniformly distributed <code>long</code>
* value between <code>lowerBound</code> and <code>upperBound</code>
* from this random number generator's sequence (exclusive of the interval
* endpoint values).
*/
public long nextLongInRange(long lowerBound, long upperBound) {
if (upperBound <= lowerBound) {
throw new IllegalArgumentException("RandomGenerator.nextLongInRange : the upper bound of the " + "random number range should be greater than the lower bound.");
}
return ((long) (nextDouble() * (upperBound - lowerBound + 1)) + lowerBound);
}
/**
* Returns the next pseudorandom, uniformly distributed <code>int</code>
* value between <code>lowerBound</code> and <code>upperBound</code>
* from this random number generator's sequence (lower bound inclusive, upper
* bout exclusive).
*/
public int nextIntInRange(int lowerBound, int upperBound) {
if (upperBound <= lowerBound) {
throw new IllegalArgumentException("RandomGenerator.nextIntInRange : the upper bound of the " + "random number range should be greater than the lower bound.");
}
return ((int) (nextDouble() * (upperBound - lowerBound + 1)) + lowerBound);
}
/** Returns a random String of the given length. */
public String nextString(int length) {
char[] chars = new char[length];
for (int i = 0; i < chars.length; i++)
chars[i] = ALPHABET.charAt(nextInt(ALPHABET.length()));
return new String(chars);
}
/**
* Returns a randomly selected integer between 0 and the length of the given
* array. Uses the given probabilities to determine the index, all values in
* this array must sum up to 1.
*/
public int randomIndex(double[] probs) {
double r = nextDouble();
double sum = 0.0d;
for (int i = 0; i < probs.length; i++) {
sum += probs[i];
if (r < sum)
return i;
}
return probs.length - 1;
}
/** Returns a random date between the given ones. */
public Date nextDateInRange(Date start, Date end) {
return new Date(nextLongInRange(start.getTime(), end.getTime()));
}
}