/// <summary> /// Normalizes the probabilities associated with an array of objects, /// then converts them into continuing sums. /// This prepares them for being usable in pickFromDistribution. /// If the probabilities are all 0, then selection is uniform, unless allowAllZeros /// is false, in which case an ArithmeticException is thrown. If any of them are negative, /// or if the distribution is empty, then an ArithmeticException is thrown. /// For example, /// {0.6, 0.4, 0.2, 0.8} -> {0.3, 0.2, 0.1, 0.4} -> {0.3, 0.5, 0.6, 1.0} /// The probabilities are retrieved and set using chooser. /// </summary> public static void OrganizeDistribution(Object[] objs, IRandomChoiceChooserD chooser, bool allowAllZeros) { // first normalize var sum = 0.0; if (objs.Length == 0) { throw new ArithmeticException("Distribution has no elements"); } foreach (var t in objs) { if (chooser.GetProbability(t) < 0.0) { throw new ArithmeticException("Distribution has negative probabilities"); } sum += chooser.GetProbability(t); } if (sum == 0.0) { if (!allowAllZeros) { throw new ArithmeticException("Distribution has all zero probabilities"); } else { foreach (var t in objs) { chooser.SetProbability(t, 1.0); } sum = objs.Length; } } foreach (var t in objs) { chooser.SetProbability(t, chooser.GetProbability(t) / sum); } // now sum sum = 0.0; foreach (var t in objs) { sum += chooser.GetProbability(t); chooser.SetProbability(t, sum); } // now we need to work backwards setting 0 values int x2; for (x2 = objs.Length - 1; x2 > 0; x2--) { if (chooser.GetProbability(objs[x2]) == chooser.GetProbability(objs[x2 - 1])) { // we're 0.0 chooser.SetProbability(objs[x2], 1.0); } else { break; } } chooser.SetProbability(objs[x2], 1.0); }