Android Open Source - dice-probabilities Sum Distribution






From Project

Back to project page dice-probabilities.

License

The source code is released under:

MIT License

If you think the Android project dice-probabilities listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.kleemann.diceprobabilities.distribution;
/*  w  w w  . j a  v a 2  s.co m*/
import org.apache.commons.math3.fraction.BigFraction;

/**
 * <p>
 * This is a distribution that is the sum of other distributions.
 * 
 * <p>
 * If event x on the distribution is the probability of getting that number then
 * each point on the sum distribution is the cumulative product of each way the
 * source distributions can be summed.
 * 
 * <p>
 * 
 * <pre>
 * Take two 6 sided dice as an example: sum.getProbability(5) =
 * d1.getProbability(1) * d2.getProbability(4) + 
 * d1.getProbability(2) * d2.getProbability(3) + 
 * d1.getProbability(3) * d2.getProbability(2) +
 * d1.getProbability(4) * d2.getProbability(1)
 * </pre>
 * 
 * <p>
 * For efficiency there is also an integer multiply function that adds a
 * distribution to itself n times.
 */
public class SumDistribution extends AbstractDistribution {

  final private BigFraction[] vals;
  final private int lower;
  final private int upper;

  /**
   * <p>
   * This is the simplest form of summation. It is guaranteed to work for
   * every input parameter but takes O(m*n) with a high constant factor due to
   * a multiply and add of a BigFraction at each iteration.
   */
  private SumDistribution(Distribution d1, Distribution d2) {
    // find the new range of the distribution.
    // It should be bounded by the sum of the lowest two values and the sum
    // of the highest two values
    lower = d1.lowerBound() + d2.lowerBound();
    upper = d1.upperBound() + d2.upperBound() - 1;

    // d6 has size of 7; max sum is 12
    // 7+7-1 equals allocation of 13 (we count zero)
    vals = new BigFraction[upper - lower];

    for (int i = 0; i < vals.length; ++i) {
      vals[i] = BigFraction.ZERO;
    }

    for (int i1 = d1.lowerBound(); i1 < d1.upperBound(); ++i1) {
      for (int i2 = d2.lowerBound(); i2 < d2.upperBound(); ++i2) {
        final int sum = i1 + i2;
        final BigFraction product = d1.getProbability(i1).multiply(
            d2.getProbability(i2));
        final int i = sum - lower;
        vals[i] = vals[i].add(product);
      }
    }
  }

  /**
   * <p>
   * Adds the distribution by itself n times. This acts like a multiply.
   * 
   * <p>
   * The method batches the adds so around log2(n) adds are made instead of n
   * adds.
   * 
   * <pre>
   * e.g. a call of multiplyLog(DiceDistribution(6), 10)
   * results in arithmetic similar to the following:
   * 
   * 2d = d.add(d)
   * 4d = 2d.add(2d)
   * 8d = 4d.add(4d)
   * 10d = 8d.add(2d)
   * 
   * </pre>
   */
  private static Distribution multiplyLog(Distribution d, int n) {
    assert (n > 1);
    // for small values of n there is no need to do anything special
    if (n < 4) {
      Distribution sum = new SumDistribution(d, d);
      for (int i = 2; i < n; ++i) {
        sum = new SumDistribution(sum, d);
      }
      return sum;
    }

    // create the multiples of two that we will use to construct the sum
    final int log = log2(n);
    Distribution sums[] = new Distribution[log + 1];
    sums[0] = d;
    for (int i = 1; i <= log; ++i) {
      sums[i] = new SumDistribution(sums[i - 1], sums[i - 1]);
    }

    // add large multiples that are less than n until the entire sum is
    // reached
    Distribution r = null;
    while (n > 0) {
      // find the largest power of 2 that is less than or equal to n
      final int lg = log2(n);
      r = r == null ? sums[lg] : new SumDistribution(r, sums[lg]);
      n -= 1 << lg;
    }
    return r;
  }

  private static int log2(int n) {
    if (n <= 0)
      throw new IllegalArgumentException();
    return 31 - Integer.numberOfLeadingZeros(n);
  }

  /**
   * <p>
   * This is a form of multiply that uses longs to multiply a distribution
   * that has equal occurrences (dice) for most of the calculations. Only
   * works for multiplies < 20 for 12 sided dice. Anything beyond that result
   * in a long underflow and an incorrect result.
   */
  private SumDistribution(DieDistribution d, int mult) {

    lower = mult;
    upper = d.getSides() * mult + 1;

    final BigFraction unit = d.getProbability(1).pow(mult);

    long[] tgt = new long[d.getSides() + 1];
    for (int i = 1; i <= d.getSides(); ++i) {
      tgt[i] = 1;
    }

    for (int m = 1; m < mult; ++m) { // loop executes mult -1 times
      long[] old = tgt.clone();
      tgt = new long[old.length + d.getSides()];
      for (int o = 0; o < old.length; ++o) {
        for (int s = 1; s <= d.getSides(); ++s) {
          final int sum = o + s;
          tgt[sum] = tgt[sum] + old[o];
        }
      }
    }

    // convert tgt to BigFraction
    vals = new BigFraction[upper - lower];
    for (int i = 0; i < vals.length; ++i) {
      vals[i] = unit.multiply(tgt[i + lower]);
    }
  }

  @Override
  public int lowerBound() {
    return lower;
  }

  @Override
  public int upperBound() {
    return upper;
  }

  @Override
  public BigFraction getProbabilityBounded(int x) {
    return vals[x - lower];
  }

  /**
   * <p>
   * Sums two distributions.
   */
  public static Distribution add(Distribution d1, Distribution d2) {
    if (d1.isZero()) {
      return d2;
    } else if (d2.isZero()) {
      return d1;
    } else {
      return new SumDistribution(d1, d2);
    }
  }

  /**
   * <p>
   * Add the given DieDistribution to itself n times. Much more efficient than
   * repeated calls to add(Distribution,Distribution)
   */
  public static Distribution multiply(DieDistribution d, int mult) {
    if (mult == 0) {
      return ConstantDistribution.ZERO;
    } else if (mult == 1) {
      return d;
    } else if (d.getSides() <= 12 && mult <= 15) {
      return new SumDistribution(d, mult);
    } else {
      return multiplyLog(d, mult);
    }
  }

  /**
   * <p>
   * A more generic form of multiply that can multiply any type of
   * Distribution not just DieDistribution.
   */
  public static Distribution multiply(Distribution d, int mult) {
    if (mult == 0) {
      return ConstantDistribution.ZERO;
    } else if (mult == 1) {
      return d;
    } else {
      return multiplyLog(d, mult);
    }
  }
}




Java Source Code List

com.asolutions.widget.RowLayout.java
org.kleemann.diceprobabilities.Check.java
org.kleemann.diceprobabilities.ConstantCurrentDicePile.java
org.kleemann.diceprobabilities.CurrentDicePile.java
org.kleemann.diceprobabilities.DiceSet.java
org.kleemann.diceprobabilities.MainActivity.java
org.kleemann.diceprobabilities.PoolDicePile.java
org.kleemann.diceprobabilities.TargetPool.java
org.kleemann.diceprobabilities.Target.java
org.kleemann.diceprobabilities.distribution.AbstractDistribution.java
org.kleemann.diceprobabilities.distribution.CachedCumulativeDistribution.java
org.kleemann.diceprobabilities.distribution.ConstantDistribution.java
org.kleemann.diceprobabilities.distribution.CritDistribution.java
org.kleemann.diceprobabilities.distribution.DeathZoneDieDistribution.java
org.kleemann.diceprobabilities.distribution.DieDistribution.java
org.kleemann.diceprobabilities.distribution.Distribution.java
org.kleemann.diceprobabilities.distribution.DogslicerDistribution.java
org.kleemann.diceprobabilities.distribution.ScaleCumulativeDistribution.java
org.kleemann.diceprobabilities.distribution.SumDistribution.java
org.kleemann.diceprobabilities.graph.GraphView.java
org.kleemann.diceprobabilities.graph.Interpolate.java
org.kleemann.diceprobabilities.graph.Point.java
org.kleemann.diceprobabilities.graph.Vector.java
org.kleemann.diceprobabilities.special.AbstractSpecial.java
org.kleemann.diceprobabilities.special.CritSpecial.java
org.kleemann.diceprobabilities.special.DeathZoneSpecial.java
org.kleemann.diceprobabilities.special.DogslicerSpecial.java
org.kleemann.diceprobabilities.special.FailureSpecial.java
org.kleemann.diceprobabilities.special.ForcedRerollSpecial.java
org.kleemann.diceprobabilities.special.ModifyEachDieSpecial.java
org.kleemann.diceprobabilities.special.NormalSpecial.java
org.kleemann.diceprobabilities.special.SecondChanceSpecial.java
org.kleemann.diceprobabilities.special.SpecialSpinner.java
org.kleemann.diceprobabilities.special.Special.java