| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.commons.rng.sampling; |
| |
| import java.util.List; |
| import java.util.Map; |
| import java.util.ArrayList; |
| |
| import org.apache.commons.rng.UniformRandomProvider; |
| import org.apache.commons.rng.sampling.distribution.GuideTableDiscreteSampler; |
| import org.apache.commons.rng.sampling.distribution.SharedStateDiscreteSampler; |
| |
| /** |
| * Sampling from a collection of items with user-defined |
| * <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution"> |
| * probabilities</a>. |
| * Note that if all unique items are assigned the same probability, |
| * it is much more efficient to use {@link CollectionSampler}. |
| * |
| * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p> |
| * |
| * @param <T> Type of items in the collection. |
| * |
| * @since 1.1 |
| */ |
| public class DiscreteProbabilityCollectionSampler<T> |
| implements SharedStateSampler<DiscreteProbabilityCollectionSampler<T>> { |
| /** The error message for an empty collection. */ |
| private static final String EMPTY_COLLECTION = "Empty collection"; |
| /** Collection to be sampled from. */ |
| private final List<T> items; |
| /** Sampler for the probabilities. */ |
| private final SharedStateDiscreteSampler sampler; |
| |
| /** |
| * Creates a sampler. |
| * |
| * @param rng Generator of uniformly distributed random numbers. |
| * @param collection Collection to be sampled, with the probabilities |
| * associated to each of its items. |
| * A (shallow) copy of the items will be stored in the created instance. |
| * The probabilities must be non-negative, but zero values are allowed |
| * and their sum does not have to equal one (input will be normalized |
| * to make the probabilities sum to one). |
| * @throws IllegalArgumentException if {@code collection} is empty, a |
| * probability is negative, infinite or {@code NaN}, or the sum of all |
| * probabilities is not strictly positive. |
| */ |
| public DiscreteProbabilityCollectionSampler(UniformRandomProvider rng, |
| Map<T, Double> collection) { |
| if (collection.isEmpty()) { |
| throw new IllegalArgumentException(EMPTY_COLLECTION); |
| } |
| |
| // Extract the items and probabilities |
| final int size = collection.size(); |
| items = new ArrayList<T>(size); |
| final double[] probabilities = new double[size]; |
| |
| int count = 0; |
| for (final Map.Entry<T, Double> e : collection.entrySet()) { |
| items.add(e.getKey()); |
| probabilities[count++] = e.getValue(); |
| } |
| |
| // Delegate sampling |
| sampler = createSampler(rng, probabilities); |
| } |
| |
| /** |
| * Creates a sampler. |
| * |
| * @param rng Generator of uniformly distributed random numbers. |
| * @param collection Collection to be sampled. |
| * A (shallow) copy of the items will be stored in the created instance. |
| * @param probabilities Probability associated to each item of the |
| * {@code collection}. |
| * The probabilities must be non-negative, but zero values are allowed |
| * and their sum does not have to equal one (input will be normalized |
| * to make the probabilities sum to one). |
| * @throws IllegalArgumentException if {@code collection} is empty or |
| * a probability is negative, infinite or {@code NaN}, or if the number |
| * of items in the {@code collection} is not equal to the number of |
| * provided {@code probabilities}. |
| */ |
| public DiscreteProbabilityCollectionSampler(UniformRandomProvider rng, |
| List<T> collection, |
| double[] probabilities) { |
| if (collection.isEmpty()) { |
| throw new IllegalArgumentException(EMPTY_COLLECTION); |
| } |
| final int len = probabilities.length; |
| if (len != collection.size()) { |
| throw new IllegalArgumentException("Size mismatch: " + |
| len + " != " + |
| collection.size()); |
| } |
| // Shallow copy the list |
| items = new ArrayList<T>(collection); |
| // Delegate sampling |
| sampler = createSampler(rng, probabilities); |
| } |
| |
| /** |
| * @param rng Generator of uniformly distributed random numbers. |
| * @param source Source to copy. |
| */ |
| private DiscreteProbabilityCollectionSampler(UniformRandomProvider rng, |
| DiscreteProbabilityCollectionSampler<T> source) { |
| this.items = source.items; |
| this.sampler = source.sampler.withUniformRandomProvider(rng); |
| } |
| |
| /** |
| * Picks one of the items from the collection passed to the constructor. |
| * |
| * @return a random sample. |
| */ |
| public T sample() { |
| return items.get(sampler.sample()); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @since 1.3 |
| */ |
| @Override |
| public DiscreteProbabilityCollectionSampler<T> withUniformRandomProvider(UniformRandomProvider rng) { |
| return new DiscreteProbabilityCollectionSampler<T>(rng, this); |
| } |
| |
| /** |
| * Creates the sampler of the enumerated probability distribution. |
| * |
| * @param rng Generator of uniformly distributed random numbers. |
| * @param probabilities Probability associated to each item. |
| * @return the sampler |
| */ |
| private static SharedStateDiscreteSampler createSampler(UniformRandomProvider rng, |
| double[] probabilities) { |
| return GuideTableDiscreteSampler.of(rng, probabilities); |
| } |
| } |