All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.generate.netgen.Distributor Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2020-2023, by Timofey Chudakov and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.generate.netgen;

import org.jgrapht.alg.util.Pair;

import java.util.*;
import java.util.function.Function;

/**
 * Distributes value units among keys given lower and upper bound constraints.
 * 

* Let's define a set of elements $\{k_1, k_2, \dots, k_n\}$. For every element a set of lower * bounds $\{l_1, l_2, \dots, l_t\}$ and upper bounds $\{u_1, u_2, \dots, u_p\}$ is specified. The * problem is to randomly distribute a number of abstract value units $V$ among keys such that the * lower bound and upper bound constraints are satisfied. This class solves this problem. * * @param the element type. * @author Timofey Chudakov * @see NetworkGenerator */ public class Distributor { /** * Random number generator used by this distributor. */ private final Random rng; /** * Lower bounds. */ private final List> lowerBounds; /** * Upper bounds. */ private final List> upperBounds; /** * Creates a Distributor using random seed. */ public Distributor() { this(System.nanoTime()); } /** * Creates a distributor using the specified {@code seed}. * * @param seed the seed for the random number generator. */ public Distributor(long seed) { this(new Random(seed)); } /** * Creates a distributor which uses the random number generatow {@code rng}. * * @param rng a random number generator to use. */ public Distributor(Random rng) { this.rng = rng; this.lowerBounds = new ArrayList<>(); this.upperBounds = new ArrayList<>(); } /** * Adds an upper bounding function. This function must be defined for all keys. * * @param upperBound an upper bound function. */ public void addUpperBound(Function upperBound) { this.upperBounds.add(upperBound); } /** * Adds a lower bound function. This function must be defined for all keys. * * @param lowerBound a lower bound function. */ public void addLowerBound(Function lowerBound) { this.lowerBounds.add(lowerBound); } /** * Finds a maximum lower bound for every key. * * @param keys list of keys. * @return the computed key lower bounds. */ private List computeLowerBounds(List keys) { List keyLowerBounds = new ArrayList<>(keys.size()); for (K key : keys) { int lowerBound = 0; for (Function lowerBoundFunction : lowerBounds) { lowerBound = Math.max(lowerBound, lowerBoundFunction.apply(key)); } keyLowerBounds.add(lowerBound); } return keyLowerBounds; } /** * Finds a minimum lower bound for every key. * * @param keys a list of keys. * @return the computed key upper bound. */ private List computeUpperBounds(List keys) { List keyUpperBounds = new ArrayList<>(keys.size()); for (K key : keys) { int upperBound = Integer.MAX_VALUE; for (Function upperBoundFunction : upperBounds) { upperBound = Math.min(upperBound, upperBoundFunction.apply(key)); } keyUpperBounds.add(upperBound); } return keyUpperBounds; } /** * Computes a suffix sum of the {@code bounds}. Returns computed suffix sum and the sum of all * elements in the {@code bounds list}. * * @param bounds list of integers. * @return computed pair of suffix sum list and a sum of all elements. */ private Pair, Long> computeSuffixSum(List bounds) { List suffixSum = new ArrayList<>(Collections.nCopies(bounds.size(), 0)); long sum = 0; for (int i = bounds.size() - 1; i >= 0; i--) { suffixSum.set(i, (int) Math.min(Integer.MAX_VALUE, sum)); sum += bounds.get(i); } return Pair.of(suffixSum, sum); } /** * Computes and returns a value distribution for the list of keys. The resulting distribution * will satisfy the (possibly empty) sets of lower and upper bound constraints. Distributed * values will be in the same order as the keys in the key list. * * @param keys the list of keys. * @param valueNum the number of abstract value units to distribute. * @return the computed value distribution. */ public List getDistribution(List keys, final int valueNum) { List keyLowerBounds = computeLowerBounds(keys); List keyUpperBounds = computeUpperBounds(keys); Pair, Long> lbSufSumP = computeSuffixSum(keyLowerBounds); Pair, Long> ubSufSumP = computeSuffixSum(keyUpperBounds); List lbSufSum = lbSufSumP.getFirst(); List ubSufSum = ubSufSumP.getFirst(); long lbSum = lbSufSumP.getSecond(); long ubSum = ubSufSumP.getSecond(); if (lbSum > valueNum) { throw new IllegalArgumentException( "Can't distribute values among keys: the sum of lower bounds is greater than the number of values"); } else if (ubSum < valueNum) { throw new IllegalArgumentException( "Can't distribute values among keys: the sum of upper bounds is smaller than the number of values"); } int remainingValues = valueNum; List resultingDistribution = new ArrayList<>(); for (int i = 0; i < keyLowerBounds.size(); i++) { int lowerBound = keyLowerBounds.get(i); int upperBound = keyUpperBounds.get(i); int valueNumUpperBound = remainingValues - lbSufSum.get(i); int valueNumLowerBound = remainingValues - ubSufSum.get(i); lowerBound = Math.max(lowerBound, valueNumLowerBound); upperBound = Math.min(upperBound, valueNumUpperBound); if (lowerBound > upperBound) { throw new IllegalArgumentException( "Infeasible bound specified for the key: " + keys.get(i)); } int allocatedValues = rng.nextInt(upperBound - lowerBound + 1) + lowerBound; resultingDistribution.add(allocatedValues); remainingValues -= allocatedValues; } return resultingDistribution; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy