/* * JASA Java Auction Simulator API * Copyright (C) 2013 Steve Phelps * * 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 2 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. */ package net.sourceforge.jabm.prng; import java.io.Serializable; import java.util.TreeMap; import java.util.TreeSet; import net.sourceforge.jabm.util.MathUtil; import net.sourceforge.jabm.util.Resetable; import org.apache.log4j.Logger; import cern.jet.random.engine.RandomEngine; /** * A class representing a discrete probability distribution which can used to * generate random events according to the specified distribution. The output * from a uniform PRNG is used to to select from the different possible events. * * @author Steve Phelps * @version $Revision: 104 $ */ public class DiscreteProbabilityDistribution implements Resetable, Serializable { /** * The probability distribution. */ protected TreeSet<ProbabilityActionPair> p; protected TreeMap<Integer, Double> reverseMap; /** * The number of possible events for this distribution. */ protected int n; protected RandomEngine prng; /** * The log4j logger. */ static Logger logger = Logger .getLogger(DiscreteProbabilityDistribution.class); /** * Construct a new distribution with k possible events. * * @param k * The number of possible events for this random variable */ public DiscreteProbabilityDistribution(RandomEngine prng, int n) { this.prng = prng; this.n = n; initialise(); } public void initialise() { p = new TreeSet<ProbabilityActionPair>(); reverseMap = new TreeMap<Integer,Double>(); for (int i = 0; i < n; i++) { double probability = 1.0 / (double) n; p.add(new ProbabilityActionPair(probability, i)); reverseMap.put(i, probability); } } public void initialise(double[] probs) { p = new TreeSet<ProbabilityActionPair>(); reverseMap = new TreeMap<Integer,Double>(); for (int i = 0; i < n; i++) { double probability = probs[i]; p.add(new ProbabilityActionPair(probability, i)); reverseMap.put(i, probability); } } public DiscreteProbabilityDistribution(RandomEngine prng, double[] p) { this.prng = prng; this.n = p.length; initialise(p); } /** * Set the probability of the ith event. * * @param i * The event * @param probability * The probability of event i occuring */ public void setProbability(int i, double probability) { double oldProbability = reverseMap.get(i); p.remove(new ProbabilityActionPair(oldProbability, i)); reverseMap.remove(i); p.add(new ProbabilityActionPair(probability, i)); reverseMap.put(i, probability); } /** * Get the probability of the ith event. * * @param i * The event */ public double getProbability(int i) { return reverseMap.get(i); } /** * Generate a random event according to the probability distribution. * * @return An integer value representing one of the possible events. */ public int generateRandomEvent() { double rand = prng.raw(); double cummProb = 0; assert MathUtil.approxEqual(getSum(), 1); for(ProbabilityActionPair pair : p) { cummProb += pair.probability; if (rand <= cummProb) { return pair.action; } } throw new ProbabilityError(this); } public double getSum() { double sum = 0; for(ProbabilityActionPair pair : p) { sum += pair.probability; } return sum; } public void reset() { initialise(); } /** * Compute the expected value of the random variable defined by this * distribution. * * @return The expected value of the distribution */ public double computeMean() { double total = 0; for (int i = 0; i < n; i++) { total += i * reverseMap.get(i); } return total; } public int getN() { return n; } public String toString() { StringBuffer s = new StringBuffer("(" + getClass()); for (int i = 0; i < n; i++) { s.append(" p[" + i + "]:" + reverseMap.get(i)); } s.append(")"); return s.toString(); } public static class ProbabilityError extends Error { public ProbabilityError(DiscreteProbabilityDistribution p) { super("Probabilities do not sum to 1: " + p); } } public class ProbabilityActionPair implements Comparable<ProbabilityActionPair> { protected double probability; protected int action; public ProbabilityActionPair(double probability, int action) { super(); this.probability = probability; this.action = action; } public ProbabilityActionPair(int action) { this.action = action; } @Override public boolean equals(Object obj) { ProbabilityActionPair other = (ProbabilityActionPair) obj; return this.probability == other.probability && this.action == other.action; } @Override public int compareTo(ProbabilityActionPair o) { if (this.probability < o.probability) { return -1; } else if (this.probability > o.probability) { return +1; } else if (this.action == o.action) { return 0; } else if (this.action > o.action) { return +1; } else { return -1; } } @Override public String toString() { return "ProbabilityActionPair [probability=" + probability + ", action=" + action + "]"; } } }