ItEr37S08CUCreacionUnidadesPlanificacionItEr36S11: Adding ProportionalDistributor that can handle integers
This commit is contained in:
parent
3520cd34dc
commit
b914f9ed0e
2 changed files with 261 additions and 0 deletions
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.navalplanner.business.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
*/
|
||||
public class ProportionalDistributor {
|
||||
|
||||
public static ProportionalDistributor create(int... initialShares) {
|
||||
return new ProportionalDistributor(toProportions(
|
||||
sumIntegerParts(initialShares), initialShares));
|
||||
}
|
||||
|
||||
private static int sumIntegerParts(int[] numbers) {
|
||||
int sum = 0;
|
||||
for (Number each : numbers) {
|
||||
sum += each.intValue();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static BigDecimal[] toProportions(int initialTotal, int... shares) {
|
||||
BigDecimal total = new BigDecimal(initialTotal);
|
||||
BigDecimal[] result = new BigDecimal[shares.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = new BigDecimal(shares[i]).divide(total, 4,
|
||||
RoundingMode.DOWN);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class ProportionWithPosition implements
|
||||
Comparable<ProportionWithPosition> {
|
||||
|
||||
public static List<ProportionWithPosition> transform(
|
||||
BigDecimal[] proportions) {
|
||||
List<ProportionWithPosition> result = new ArrayList<ProportionWithPosition>();
|
||||
for (int i = 0; i < proportions.length; i++) {
|
||||
result.add(new ProportionWithPosition(i, proportions[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
final int position;
|
||||
final BigDecimal proportion;
|
||||
|
||||
ProportionWithPosition(int position, BigDecimal proportion) {
|
||||
this.position = position;
|
||||
this.proportion = proportion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ProportionWithPosition other) {
|
||||
return proportion.compareTo(other.proportion);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final BigDecimal[] proportions;
|
||||
|
||||
private ProportionalDistributor(BigDecimal[] proportions) {
|
||||
this.proportions = proportions;
|
||||
}
|
||||
|
||||
public int[] distribute(final int total) {
|
||||
int[] result = new int[proportions.length];
|
||||
int remaining = total - assignIntegerParts(total, result);
|
||||
if (remaining == 0) {
|
||||
return result;
|
||||
}
|
||||
BigDecimal[] currentProportions = toProportions(total, result);
|
||||
assignRemaining(result, currentProportions, remaining);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int assignIntegerParts(int current, int[] result) {
|
||||
int substract = 0;
|
||||
for (int i = 0; i < proportions.length; i++) {
|
||||
int intValue = proportions[i].multiply(new BigDecimal(current))
|
||||
.intValue();
|
||||
if (intValue > 0) {
|
||||
result[i] = result[i] + intValue;
|
||||
substract += intValue;
|
||||
}
|
||||
}
|
||||
return substract;
|
||||
}
|
||||
|
||||
private void assignRemaining(int[] result, BigDecimal[] currentProportions,
|
||||
int remaining) {
|
||||
List<ProportionWithPosition> transform = ProportionWithPosition
|
||||
.transform(difference(currentProportions));
|
||||
Collections.sort(transform, Collections.reverseOrder());
|
||||
for (int i = 0; i < remaining; i++) {
|
||||
ProportionWithPosition proportionWithPosition = transform.get(i);
|
||||
result[proportionWithPosition.position] = result[proportionWithPosition.position] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal[] difference(BigDecimal[] pr) {
|
||||
BigDecimal[] result = new BigDecimal[proportions.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = proportions[i].subtract(pr[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.navalplanner.business.common;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
*
|
||||
*/
|
||||
public class ProportionalDistributorTest {
|
||||
|
||||
@Test
|
||||
public void mustGiveTheSameDistributionForSameTotal() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
100, 200);
|
||||
assertThat(distributor.distribute(300), equalToDistribution(100, 200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactDivisionsWorkOk() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
100, 100, 100);
|
||||
assertThat(distributor.distribute(600), equalToDistribution(200, 200,
|
||||
200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distributingZeroGivesZeroShares() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
100, 100, 100);
|
||||
assertThat(distributor.distribute(0), equalToDistribution(0, 0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifOneOfTheProportionsIsZeroAlwaysGivesZeros() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
100, 100, 0);
|
||||
assertThat(distributor.distribute(100), equalToDistribution(50, 50, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disputedPartGoesToFirstIfEqualWeight() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
10, 10, 10);
|
||||
assertThat(distributor.distribute(10), equalToDistribution(4, 3, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distributionIsKept() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(2,
|
||||
3, 5);
|
||||
assertThat(distributor.distribute(1), equalToDistribution(0, 0, 1));
|
||||
assertThat(distributor.distribute(2), equalToDistribution(0, 1, 1));
|
||||
assertThat(distributor.distribute(3), equalToDistribution(1, 1, 1));
|
||||
assertThat(distributor.distribute(4), equalToDistribution(1, 1, 2));
|
||||
assertThat(distributor.distribute(5), equalToDistribution(1, 2, 2));
|
||||
assertThat(distributor.distribute(6), equalToDistribution(1, 2, 3));
|
||||
assertThat(distributor.distribute(10), equalToDistribution(2, 3, 5));
|
||||
assertThat(distributor.distribute(7), equalToDistribution(1, 2, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addingOneEachTime() {
|
||||
ProportionalDistributor distributor = ProportionalDistributor.create(
|
||||
99, 101, 800);
|
||||
assertThat(distributor.distribute(1), equalToDistribution(0, 0, 1));
|
||||
assertThat(distributor.distribute(3), equalToDistribution(0, 0, 3));
|
||||
assertThat(distributor.distribute(6), equalToDistribution(0, 1, 5));
|
||||
assertThat(distributor.distribute(7), equalToDistribution(1, 1, 5));
|
||||
assertThat(distributor.distribute(8), equalToDistribution(1, 1, 6));
|
||||
assertThat(distributor.distribute(9), equalToDistribution(1, 1, 7));
|
||||
assertThat(distributor.distribute(10), equalToDistribution(1, 1, 8));
|
||||
assertThat(distributor.distribute(11), equalToDistribution(1, 1, 9));
|
||||
assertThat(distributor.distribute(12), equalToDistribution(1, 1, 10));
|
||||
assertThat(distributor.distribute(13), equalToDistribution(1, 1, 11));
|
||||
assertThat(distributor.distribute(14), equalToDistribution(1, 2, 11));
|
||||
assertThat(distributor.distribute(15), equalToDistribution(1, 2, 12));
|
||||
assertThat(distributor.distribute(16), equalToDistribution(1, 2, 13));
|
||||
assertThat(distributor.distribute(17), equalToDistribution(2, 2, 13));
|
||||
assertThat(distributor.distribute(20), equalToDistribution(2, 2, 16));
|
||||
}
|
||||
|
||||
private static Matcher<int[]> equalToDistribution(final int... distribution) {
|
||||
return new BaseMatcher<int[]>() {
|
||||
|
||||
@Override
|
||||
public boolean matches(Object object) {
|
||||
if (object instanceof int[]) {
|
||||
int[] arg = (int[]) object;
|
||||
return Arrays.equals(arg, distribution);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("must equal "
|
||||
+ Arrays.toString(distribution));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue