blob: f9339ef9cf17398edd8f4553150ae8a2ea6855ff [file] [log] [blame]
// ============================================================================
// Copyright 2006-2012 Daniel W. Dyer
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
package org.uncommons.maths.demo;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.uncommons.maths.number.NumberGenerator;
/**
* Encapsulates a probability distribution. Provides both theoretical
* values for the distribution as well as a way of randomly generating
* values that follow this distribution.
* @author Daniel Dyer
*/
abstract class ProbabilityDistribution
{
protected abstract NumberGenerator<?> createValueGenerator(Random rng);
public Map<Double, Double> generateValues(int count,
Random rng)
{
Map<Double, Double> values = isDiscrete()
? generateDiscreteValues(count, rng)
: generateContinuousValues(count, rng);
double sum = 0;
for (Double key : values.keySet())
{
Double value = values.get(key);
values.put(key, value / count);
sum += value;
}
assert Math.round(sum) == count : "Wrong total: " + sum;
return values;
}
private Map<Double, Double> generateDiscreteValues(int count,
Random rng)
{
NumberGenerator<?> generator = createValueGenerator(rng);
Map<Double, Double> values = new HashMap<Double, Double>();
for (int i = 0; i < count; i++)
{
double value = generator.nextValue().doubleValue();
Double aggregate = values.get(value);
aggregate = aggregate == null ? 0 : aggregate;
values.put(value, ++aggregate);
}
return values;
}
private Map<Double, Double> generateContinuousValues(int count,
Random rng)
{
NumberGenerator<?> generator = createValueGenerator(rng);
double[] values = new double[count];
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (int i = 0; i < count; i++)
{
double value = generator.nextValue().doubleValue();
min = Math.min(value, min);
max = Math.max(value, max);
values[i] = value;
}
return doQuantization(max, min, values);
}
/**
* Convert the continuous values into discrete values by chopping up
* the distribution into several equally-sized intervals.
*/
protected static Map<Double, Double> doQuantization(double max,
double min,
double[] values)
{
double range = max - min;
int noIntervals = 20;
double intervalSize = range / noIntervals;
int[] intervals = new int[noIntervals];
for (double value : values)
{
int interval = Math.min(noIntervals - 1,
(int) Math.floor((value - min) / intervalSize));
assert interval >= 0 && interval < noIntervals : "Invalid interval: " + interval;
++intervals[interval];
}
Map<Double, Double> discretisedValues = new HashMap<Double, Double>();
for (int i = 0; i < intervals.length; i++)
{
// Correct the value to take into account the size of the interval.
double value = (1 / intervalSize) * (double) intervals[i];
discretisedValues.put(min + ((i + 0.5) * intervalSize), value);
}
return discretisedValues;
}
public abstract Map<Double, Double> getExpectedValues();
public abstract double getExpectedMean();
public abstract double getExpectedStandardDeviation();
public abstract String getDescription();
public abstract boolean isDiscrete();
}