Merging changes from mk-branch 122:123.
This commit is contained in:
parent
b34c349e00
commit
8ab56480a3
@ -4,8 +4,10 @@ package eva2;
|
||||
* Main product and version information strings.
|
||||
*
|
||||
* --- Changelog
|
||||
* 2.028: Tuned the Population to sort only when necessary on calls to getBestN... Added StatisticsDummy.
|
||||
* Slightly tuned SimpleProblemWrapper to call initProblem of simple problems if available.
|
||||
* 2.027: Renamed SetData and SetDataLamarckian from individual datatype interfaces to SetGenotype and SetPhenotype.
|
||||
* Repaired the GenericArrayEditor. Population measures can now be plottet in stats.
|
||||
* Repaired the GenericArrayEditor. Population measures can now be plotted in stats.
|
||||
* 2.026: Added DiversityTerminator and KnownOptimaTerminator, slightly changed InterfaceTerminator for these
|
||||
* and InterfaceStatistics to provide termination message to text window.
|
||||
* Killed redundant method getGenerations() in Population. Population.getAllSolutions now returns a
|
||||
@ -25,7 +27,7 @@ package eva2;
|
||||
public class EvAInfo {
|
||||
public static final String productName = "EvA 2";
|
||||
public static final String productLongName = "Evolutionary Algorithms Workbench 2";
|
||||
public static final String versionNum = new String ("2.027");
|
||||
public static final String versionNum = new String ("2.028");
|
||||
public static final String url = "http://www.ra.cs.uni-tuebingen.de/software/EvA2";
|
||||
|
||||
public static final String propertyFile = "resources/EvA2.props";
|
||||
|
@ -16,6 +16,7 @@ import eva2.server.go.populations.Population;
|
||||
import eva2.server.modules.GOParameters;
|
||||
import eva2.server.modules.Processor;
|
||||
import eva2.server.stat.AbstractStatistics;
|
||||
import eva2.server.stat.StatisticsDummy;
|
||||
import eva2.server.stat.InterfaceTextListener;
|
||||
import eva2.server.stat.StatisticsStandalone;
|
||||
|
||||
@ -33,13 +34,42 @@ public class OptimizerRunnable implements Runnable {
|
||||
private boolean postProcessOnly = false;
|
||||
private InterfaceTextListener listener = null;
|
||||
|
||||
/**
|
||||
* Construct an OptimizerRunnable with given parameters and a StatisticsStandalone instance without restart,
|
||||
* meaning that the population will be initialized randomly.
|
||||
*
|
||||
* @param params
|
||||
* @param outputFilePrefix
|
||||
*/
|
||||
public OptimizerRunnable(GOParameters params, String outputFilePrefix) {
|
||||
this(params, outputFilePrefix, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor assumes that DummyStatistics are enough. This saves time e.g. for small populations.
|
||||
* If restart is true, the processor will not reinitialize the population allowing search on predefined populations.
|
||||
*
|
||||
* @param params
|
||||
* @param restart
|
||||
*/
|
||||
public OptimizerRunnable(GOParameters params, boolean restart) {
|
||||
proc = new Processor(new StatisticsDummy(), null, params);
|
||||
if (proc.getStatistics() instanceof AbstractStatistics) ((AbstractStatistics)proc.getStatistics()).setSaveParams(false);
|
||||
doRestart = restart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an OptimizerRunnable with given parameters and a StatisticsStandalone instance with optional restart.
|
||||
* If restart is true, the processor will not reinitialize the population allowing search on predefined populations.
|
||||
* The outputFilePrefix may be null.
|
||||
*
|
||||
* @param params
|
||||
* @param outputFilePrefix
|
||||
* @param restart
|
||||
*/
|
||||
public OptimizerRunnable(GOParameters params, String outputFilePrefix, boolean restart) {
|
||||
proc = new Processor(new StatisticsStandalone(outputFilePrefix), null, params);
|
||||
((AbstractStatistics)proc.getStatistics()).setSaveParams(false);
|
||||
if (proc.getStatistics() instanceof AbstractStatistics) ((AbstractStatistics)proc.getStatistics()).setSaveParams(false);
|
||||
doRestart = restart;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ public class MutateESMutativeStepSizeControl implements InterfaceMutation, java.
|
||||
if (range[i][1] < x[i]) x[i] = range[i][1];
|
||||
}
|
||||
((InterfaceESIndividual)individual).SetDGenotype(x);
|
||||
System.out.println("new step size: " + m_MutationStepSize);
|
||||
// System.out.println("new step size: " + m_MutationStepSize);
|
||||
}
|
||||
//System.out.println("After Mutate: " +((GAIndividual)individual).getSolutionRepresentationFor());
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import eva2.server.go.problems.FM0Problem;
|
||||
import eva2.server.go.problems.InterfaceMultimodalProblemKnown;
|
||||
import eva2.server.go.strategies.HillClimbing;
|
||||
import eva2.server.stat.InterfaceTextListener;
|
||||
import eva2.server.stat.StatsParameter;
|
||||
import eva2.tools.Pair;
|
||||
|
||||
|
||||
@ -371,8 +372,9 @@ public class PostProcess {
|
||||
}
|
||||
hc.setPopulation(pop);
|
||||
// hc.initByPopulation(pop, false);
|
||||
hcRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(hc, pop, problem, 0, term), null, true);
|
||||
hcRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(hc, pop, problem, 0, term), true);
|
||||
hcRunnable.getGOParams().setDoPostProcessing(false);
|
||||
hcRunnable.setVerbosityLevel(StatsParameter.VERBOSITY_NONE);
|
||||
hcRunnable.run();
|
||||
hcRunnable.getGOParams().setDoPostProcessing(true);
|
||||
hcRunnable = null;
|
||||
|
@ -34,6 +34,8 @@ public class Population extends ArrayList implements PopulationInterface, Clonea
|
||||
protected int m_FunctionCalls = 0;
|
||||
protected int m_Size = 50;
|
||||
protected Population m_Archive = null;
|
||||
transient private ArrayList<AbstractEAIndividual> sortedArr = null;
|
||||
private int lastQModCount = -1;
|
||||
transient protected InterfacePopulationChangedEventListener m_Listener = null;
|
||||
protected int notifyEvalInterval = 0;
|
||||
protected HashMap<String, Object> additionalPopData = null;
|
||||
@ -161,7 +163,7 @@ public class Population extends ArrayList implements PopulationInterface, Clonea
|
||||
useHistory = useHist;
|
||||
}
|
||||
|
||||
/** This method will allow cou to increment the current number of function calls.
|
||||
/** This method will allow you to increment the current number of function calls.
|
||||
*/
|
||||
public void incrFunctionCalls() {
|
||||
this.m_FunctionCalls++;
|
||||
@ -178,17 +180,23 @@ public class Population extends ArrayList implements PopulationInterface, Clonea
|
||||
*/
|
||||
public void incrFunctionCallsby(int d) {
|
||||
if (doEvalNotify()) {
|
||||
System.out.println("checking funcall event...");
|
||||
int nextStep = ((m_FunctionCalls/notifyEvalInterval)+1) * notifyEvalInterval; // next interval boundary
|
||||
if (nextStep <= (m_FunctionCalls+d)) {
|
||||
// System.out.println("checking funcall event...");
|
||||
int nextStep; // next interval boundary
|
||||
while ((nextStep = calcNextBoundary()) <= (m_FunctionCalls+d)) {
|
||||
// the notify interval will be stepped over or hit
|
||||
int toHit = (nextStep - m_FunctionCalls);
|
||||
this.m_FunctionCalls += toHit; // little cheat, notify may be after some more evals
|
||||
firePropertyChangedEvent(funCallIntervalReached);
|
||||
this.m_FunctionCalls += (d-toHit);
|
||||
d = d-toHit;
|
||||
// this.m_FunctionCalls += (d-toHit);
|
||||
}
|
||||
if (d>0) this.m_FunctionCalls += d; // add up the rest
|
||||
} else this.m_FunctionCalls += d;
|
||||
}
|
||||
|
||||
private int calcNextBoundary() {
|
||||
return ((m_FunctionCalls/notifyEvalInterval)+1) * notifyEvalInterval;
|
||||
}
|
||||
|
||||
/** Something has changed
|
||||
*/
|
||||
@ -363,32 +371,69 @@ public class Population extends ArrayList implements PopulationInterface, Clonea
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the n current best individuals from the population, where
|
||||
* This method returns the n currently best individuals from the population, where
|
||||
* the sorting criterion is delivered by an AbstractEAIndividualComparator.
|
||||
* There are less than n individuals returned if the population is smaller than n.
|
||||
* If n is <= 0, then all individuals are returned and effectively just sorted
|
||||
* by fitness.
|
||||
* The comparator does not check constraints!
|
||||
*
|
||||
* @param n number of individuals to look out for
|
||||
* @return The m best individuals, where m <= n
|
||||
*
|
||||
*/
|
||||
public Population getBestNIndividuals(int n) {
|
||||
if (n <= 0) n = super.size();
|
||||
return getSortedNIndividuals(n, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the n current best individuals from the population, where
|
||||
* the sorting criterion is delivered by an AbstractEAIndividualComparator.
|
||||
* There are less than n individuals returned if the population is smaller than n.
|
||||
* The comparator does not check constraints!
|
||||
*
|
||||
* @param n number of individuals to look out for
|
||||
* @param bBestOrWorst if true, the best n are returned, else the worst n individuals
|
||||
* @return The m sorted best or worst individuals, where m <= n
|
||||
*
|
||||
*/
|
||||
public Population getSortedNIndividuals(int n, boolean bBestOrWorst) {
|
||||
if ((n < 0) || (n>super.size())) {
|
||||
System.err.println("invalid request to getSortedNIndividuals: n="+n + ", size is " + super.size());
|
||||
n = super.size();
|
||||
}
|
||||
int skip = 0;
|
||||
if (!bBestOrWorst) skip = super.size()-n;
|
||||
|
||||
Population result = new Population(n);
|
||||
PriorityQueue<AbstractEAIndividual> queue = new PriorityQueue<AbstractEAIndividual>(super.size(), new AbstractEAIndividualComparator());
|
||||
ArrayList<AbstractEAIndividual> sorted = getSorted();
|
||||
|
||||
for (int i = 0; i < super.size(); i++) {
|
||||
queue.add(getEAIndividual(i));
|
||||
}
|
||||
for (int i = 0; i<n ; i++) {
|
||||
if (queue.size() == 0) break;
|
||||
result.add(queue.poll());
|
||||
for (int i = skip; i < skip+n; i++) {
|
||||
result.add(sorted.get(i));
|
||||
}
|
||||
result.setPopulationSize(result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids having to sort again in several calls without modifications in between.
|
||||
* The returned array should not be modified!
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected ArrayList<AbstractEAIndividual> getSorted() {
|
||||
if (sortedArr == null || (super.modCount != lastQModCount)) {
|
||||
PriorityQueue<AbstractEAIndividual> sQueue = new PriorityQueue<AbstractEAIndividual>(super.size(), new AbstractEAIndividualComparator());
|
||||
for (int i = 0; i < super.size(); i++) {
|
||||
sQueue.add(getEAIndividual(i));
|
||||
}
|
||||
lastQModCount = super.modCount;
|
||||
if (sortedArr==null) sortedArr = new ArrayList<AbstractEAIndividual>(this.size());
|
||||
else sortedArr.clear();
|
||||
AbstractEAIndividual indy;
|
||||
while ((indy=sQueue.poll())!=null) sortedArr.add(indy);
|
||||
}
|
||||
return sortedArr;
|
||||
}
|
||||
|
||||
/** This method returns n random best individuals from the population.
|
||||
*
|
||||
* @param n number of individuals to look out for
|
||||
@ -662,6 +707,15 @@ public class Population extends ArrayList implements PopulationInterface, Clonea
|
||||
return addIndividual((IndividualInterface)o);
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayList does not increase the modCount in set. Why???
|
||||
*/
|
||||
public Object set(int index, Object element) {
|
||||
Object prev = super.set(index, element);
|
||||
modCount++;
|
||||
return prev;
|
||||
}
|
||||
|
||||
public boolean addIndividual(IndividualInterface ind) {
|
||||
super.add(ind);
|
||||
return true;
|
||||
|
@ -50,6 +50,7 @@ public class F1Problem extends AbstractProblemDouble implements Interface2DBorde
|
||||
*/
|
||||
public void initProblem() {
|
||||
// this.m_OverallBest = null;
|
||||
initTemplate();
|
||||
}
|
||||
|
||||
protected double[] getEvalArray(AbstractEAIndividual individual){
|
||||
|
@ -1,7 +1,7 @@
|
||||
package eva2.server.go.problems;
|
||||
|
||||
import eva2.server.go.individuals.ESIndividualDoubleData;
|
||||
import eva2.server.go.strategies.ParticleSwarmOptimization;
|
||||
import eva2.tools.Mathematics;
|
||||
import wsi.ra.math.Jama.Matrix;
|
||||
|
||||
/**
|
||||
@ -32,10 +32,12 @@ public class F6Problem extends F1Problem implements InterfaceMultimodalProblem,
|
||||
*/
|
||||
public void initProblem() {
|
||||
super.initProblem();
|
||||
rotation = new Matrix(m_ProblemDimension, m_ProblemDimension);
|
||||
Matrix vec = new Matrix(m_ProblemDimension, 1);
|
||||
for (int i=0; i<m_ProblemDimension; i++) vec.set(i,0, i+1);
|
||||
rotation = ParticleSwarmOptimization.getRotationMatrix(vec).transpose();
|
||||
if (doRotation) {
|
||||
rotation = new Matrix(m_ProblemDimension, m_ProblemDimension);
|
||||
Matrix vec = new Matrix(m_ProblemDimension, 1);
|
||||
for (int i=0; i<m_ProblemDimension; i++) vec.set(i,0, i+1);
|
||||
rotation = Mathematics.getRotationMatrix(vec).transpose();
|
||||
} else rotation = null;
|
||||
}
|
||||
|
||||
/** This method returns a deep clone of the problem.
|
||||
|
@ -20,6 +20,7 @@ import eva2.server.go.problems.Interface2DBorderProblem;
|
||||
import eva2.server.go.problems.InterfaceOptimizationProblem;
|
||||
import wsi.ra.math.RNG;
|
||||
import eva2.tools.EVAERROR;
|
||||
import eva2.tools.Mathematics;
|
||||
import eva2.tools.SelectedTag;
|
||||
|
||||
import wsi.ra.chart2d.DPoint;
|
||||
@ -797,7 +798,7 @@ public class ParticleSwarmOptimization implements InterfaceOptimizer, java.io.Se
|
||||
for (int i=1; i<dim; i++) {
|
||||
randVec.set(i, 0, project(-len/2, len/2, RNG.gaussianDouble(len/(scale*2))));
|
||||
}
|
||||
Matrix rotation = getRotationMatrix(dir);
|
||||
Matrix rotation = Mathematics.getRotationMatrix(dir);
|
||||
rotation = rotation.transpose();
|
||||
//printMatrix(rotation);
|
||||
resVec = rotation.times(randVec);
|
||||
@ -860,75 +861,6 @@ public class ParticleSwarmOptimization implements InterfaceOptimizer, java.io.Se
|
||||
// vec.setElement(j, val1);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Return a matrix A which performs the rotation of vec to (1,0,0,...0) if forward is true, else
|
||||
* return a matrix B which performs the reverted rotation, where B=A' (transposition).
|
||||
*
|
||||
* @param vec
|
||||
* @return
|
||||
*/
|
||||
public static Matrix getRotationMatrix(Matrix vec) {
|
||||
Matrix A = Matrix.identity(vec.getRowDimension(), vec.getRowDimension());
|
||||
Matrix tmp = Matrix.identity(vec.getRowDimension(), vec.getRowDimension());
|
||||
Matrix z = (Matrix)vec.clone();
|
||||
|
||||
double w, cosw, sinw;
|
||||
|
||||
z.multi(z.norm2()); // normalize
|
||||
|
||||
|
||||
for (int i=1; i<vec.getRowDimension(); i++) {
|
||||
w = Math.atan2(z.get(i,0), z.get(0,0));// calc angle between the projection of x and x0 in x0-xi-plane
|
||||
|
||||
cosw = Math.cos(w);
|
||||
sinw = Math.sin(w);
|
||||
tmp.set(0, 0, cosw); // make partial rotation matrix
|
||||
tmp.set(0, i, sinw);
|
||||
tmp.set(i, 0, -sinw);
|
||||
tmp.set(i, i, cosw);
|
||||
|
||||
A = tmp.times(A); // add to resulting rotation
|
||||
z = tmp.times(z); // z is now 0 in i-th component
|
||||
|
||||
tmp.set(0, 0, 1); // reset tmp matrix to unity
|
||||
tmp.set(0, i, 0);
|
||||
tmp.set(i, 0, 0);
|
||||
tmp.set(i, i, 1);
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
// protected GMatrix nrotate(GVector vec) {
|
||||
// GMatrix A = new GMatrix(vec.getSize(), vec.getSize());
|
||||
// GMatrix tmp = new GMatrix(vec.getSize(), vec.getSize());
|
||||
// GVector z = new GVector(vec);
|
||||
//
|
||||
// double w, cosw, sinw;
|
||||
//
|
||||
// z.normalize();
|
||||
//
|
||||
//
|
||||
// for (int i=1; i<vec.getSize(); i++) {
|
||||
// w = Math.atan2(z.getElement(i), z.getElement(0));// calc angle between the projection of x and x0 in x0-xi-plane
|
||||
//
|
||||
// cosw = Math.cos(w);
|
||||
// sinw = Math.sin(w);
|
||||
// tmp.setElement(0, 0, cosw); // make partial rotation matrix
|
||||
// tmp.setElement(0, i, sinw);
|
||||
// tmp.setElement(i, 0, -sinw);
|
||||
// tmp.setElement(i, i, cosw);
|
||||
//
|
||||
// A.mul(tmp, A); // add to resulting rotation
|
||||
// z.mul(tmp, z); // z is now 0 in i-th component
|
||||
//
|
||||
// tmp.setElement(0, 0, 1); // reset tmp matrix to unity
|
||||
// tmp.setElement(0, i, 0);
|
||||
// tmp.setElement(i, 0, 0);
|
||||
// tmp.setElement(i, i, 1);
|
||||
// }
|
||||
// return A;
|
||||
// }
|
||||
|
||||
/**
|
||||
* In the topology range for the given index, find the best stored individual and return its position.
|
||||
*
|
||||
|
76
src/eva2/server/stat/StatisticsDummy.java
Normal file
76
src/eva2/server/stat/StatisticsDummy.java
Normal file
@ -0,0 +1,76 @@
|
||||
package eva2.server.stat;
|
||||
|
||||
import eva2.server.go.IndividualInterface;
|
||||
import eva2.server.go.PopulationInterface;
|
||||
import eva2.server.go.problems.InterfaceAdditionalPopulationInformer;
|
||||
|
||||
/**
|
||||
* This may be given to a Processor if no further stats are required. It speeds up
|
||||
* optimization especially with small populations (e.g. HC as local search operator).
|
||||
*
|
||||
* @author mkron
|
||||
*
|
||||
*/
|
||||
public class StatisticsDummy implements InterfaceStatistics, InterfaceTextListener {
|
||||
boolean consoleOut = false;
|
||||
StatsParameter sParams = null;
|
||||
|
||||
public StatisticsDummy() {
|
||||
sParams = new StatsParameter();
|
||||
sParams.setOutputVerbosityK(StatsParameter.VERBOSITY_NONE);
|
||||
}
|
||||
|
||||
public StatisticsDummy(boolean doConsoleOut) {
|
||||
sParams = new StatsParameter();
|
||||
sParams.setOutputVerbosityK(StatsParameter.VERBOSITY_NONE);
|
||||
consoleOut = doConsoleOut;
|
||||
}
|
||||
|
||||
public void addTextListener(InterfaceTextListener listener) {
|
||||
System.err.println("addTextListener not provided!");
|
||||
}
|
||||
|
||||
public void createNextGenerationPerformed(PopulationInterface Pop,
|
||||
InterfaceAdditionalPopulationInformer informer) {
|
||||
}
|
||||
|
||||
public void createNextGenerationPerformed(double[] bestfit,
|
||||
double[] worstfit, int calls) {
|
||||
}
|
||||
|
||||
public double[] getBestFitness() {
|
||||
System.err.println("getBestFitness not provided!");
|
||||
return null;
|
||||
}
|
||||
|
||||
public IndividualInterface getBestSolution() {
|
||||
System.err.println("getBestSolution not provided!");
|
||||
return null;
|
||||
}
|
||||
|
||||
public InterfaceStatisticsParameter getStatisticsParameter() {
|
||||
return sParams;
|
||||
}
|
||||
|
||||
public void printToTextListener(String s) {
|
||||
if (consoleOut) System.out.println(s);
|
||||
}
|
||||
|
||||
public boolean removeTextListener(InterfaceTextListener listener) {
|
||||
System.err.println("removeTextListener not provided!");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void startOptPerformed(String InfoString, int runnumber,
|
||||
Object params) {}
|
||||
|
||||
public void stopOptPerformed(boolean normal, String stopMessage) {}
|
||||
|
||||
public void print(String str) {
|
||||
if (consoleOut) System.out.print(str);
|
||||
}
|
||||
public void println(String str) {
|
||||
if (consoleOut) System.out.println(str);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package eva2.tools;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import wsi.ra.math.Jama.Matrix;
|
||||
import wsi.ra.math.interpolation.BasicDataSet;
|
||||
import wsi.ra.math.interpolation.InterpolationException;
|
||||
import wsi.ra.math.interpolation.SplineInterpolation;
|
||||
@ -609,4 +611,42 @@ public class Mathematics {
|
||||
public static void normalizeSum(double[] v, double[] res) {
|
||||
svMult(1./sum(v), v, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a matrix A which performs the rotation of vec to (1,0,0,...0) if forward is true, else
|
||||
* return a matrix B which performs the reverted rotation, where B=A' (transposition).
|
||||
*
|
||||
* @param vec
|
||||
* @return
|
||||
*/
|
||||
public static Matrix getRotationMatrix(Matrix vec) {
|
||||
Matrix A = Matrix.identity(vec.getRowDimension(), vec.getRowDimension());
|
||||
Matrix tmp = Matrix.identity(vec.getRowDimension(), vec.getRowDimension());
|
||||
Matrix z = (Matrix)vec.clone();
|
||||
|
||||
double w, cosw, sinw;
|
||||
|
||||
z.multi(z.norm2()); // normalize
|
||||
|
||||
|
||||
for (int i=1; i<vec.getRowDimension(); i++) {
|
||||
w = Math.atan2(z.get(i,0), z.get(0,0));// calc angle between the projection of x and x0 in x0-xi-plane
|
||||
|
||||
cosw = Math.cos(w);
|
||||
sinw = Math.sin(w);
|
||||
tmp.set(0, 0, cosw); // make partial rotation matrix
|
||||
tmp.set(0, i, sinw);
|
||||
tmp.set(i, 0, -sinw);
|
||||
tmp.set(i, i, cosw);
|
||||
|
||||
A = tmp.times(A); // add to resulting rotation
|
||||
z = tmp.times(z); // z is now 0 in i-th component
|
||||
|
||||
tmp.set(0, 0, 1); // reset tmp matrix to unity
|
||||
tmp.set(0, i, 0);
|
||||
tmp.set(i, 0, 0);
|
||||
tmp.set(i, i, 1);
|
||||
}
|
||||
return A;
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ public class RNG extends Random {
|
||||
return (hi-lo)*random.nextFloat()+lo;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* A random double value between 0 and 1.
|
||||
*/
|
||||
public static double randomDouble() {
|
||||
return random.nextDouble();
|
||||
|
Loading…
x
Reference in New Issue
Block a user