# Set value in an array[3], that the sum of all three values is always 1.0

So I'm working on a Civ like game right now and want to have a setting how taxes are split in gold income, science and luxury goods.

private double[] rate = new double[] { 0.4, 0.6, 0.0 };


now I wanted to add a methode to change one of the values to a higher or lower value but maintain the overall sum of 1.0 (100%) but I'm stuck here with how to take or add values evenly from the other two rates

    public void setRate (int index, double r) {
if (r < 0.0) {
throw new IllegalArgumentException("Invalid Rate");
}

double diff = r - rate[index];
if (diff > 0) {

// ?

} else if (diff < 0) {

// ?

}
rate[index] = r;
}


EDIT for amitp:

public void setRate (int index, double r) {
if (rate[index] = 1.0) {
for (int i = 0; i < 3; i++) {
if (i != index) {
rate[i] = (rate[index] - r) / 2;
}
}
} else {
for (int i = 0; i < 3; i++) {
if (i != index) {
rate[i] = rate[i] * (1.0 - r) / (1.0 - rate[index]);
}
rate[index] = r;
}
}


I think the simplest approach is to rescale the values.

The values other than the one you're setting currently sum to 1.0 - rate[index]. You want them to sum to 1.0 - r. You can therefore scale each of them by (1.0 - new_value) / (1.0 - old_value) to preserve the sum.

public void setRate (int index, double r) {
for (int i = 0; i < 3; i++) {
if (i != index) {
rate[i] = rate[i] * (1.0 - r) / (1.0 - rate[index]);
}
}
rate[index] = r;
}


This solution avoids:

1. Different code for positive and negative changes
2. Having to handle underflow (something + diff going below 0.0) and overflow (something + diff going above 1.0)
3. A value going from zero to non-zero, or vice versa. If the player set something at 0, then they probably want to keep it there.

Side note: a geometric interpretation is that your three values are coordinates in a cube. You constrain x + y + z = 1.0 so that puts you on an equilateral triangle embedded in the cube. Thinking about it this way helped me come up with the above solution.

• Note that this solution can still fail, if 1.0 - rate[index] is 0.0. That will happen when 100% of the resource is assigned to one value, and you try to take away from it. I guess you'd have to handle this special case, probably by evenly distributing the remainder to the other values. If you treat 0 values as epsilon instead of 0.0 then it will work out, I think. Jul 12, 2014 at 17:42
• Would something like this work to write before the for loop? Look at the Edit. Jul 12, 2014 at 18:11
• That looks like it should work. A unit test for this function should include cases where rates before and after are 0.0 and 1.0. Jul 12, 2014 at 21:07

You could divide diff by two and then subtract it or add it accordingly to the other two.

double diff = r - rate[index];
double change = diff/2 * -1; //we need to reverse the operation

for(int i = 0 ; i < 3; i++)
{
if(index != i) rate[i] += change;
}


Still you should be careful for number precision errors. For that you could use an int[] where all values sum up to 100.

• You'll also need to handle upper and lower bounds. For example, if you start with {0.4, 0.6, 0.0} and want to increase the first to 0.5, then =change= will be -0.05. You'll end up with {0.5, 0.55, -0.05}. Jul 12, 2014 at 17:06

One way to do this is to add to the indexes' value by its self multiplied by diff. rate[i] = rate[i] + rate[i]*diff where i is the other to values other than index

    double d = 1 - rate[index]
double diff = rate[index] - r;
rate[index] = r;
index = (index + 1) % 3;           // increment and wrap around index
rate[index] += diff*rate[index]/d;
index = (index + 1) % 3;           // increment and wrap around index
rate[index] += diff*rate[index]/d;


Hope that helps.

• Say I want to raise gold income from 0% to 50% But with your solution I would add 25% to both science and luxury goods and not raise the gold income (or decrease it). Also this only works when the index is 0. If I want to change the luxury the index is 2. Then I would add 1, have 3 and get an Exception. Jul 12, 2014 at 11:42
• You are correct diff should be defined as follows diff = rate[index] - r. Also I didn't include it but right after diff is define the new rate from which ever index should be set with the following statement rate[index] = r. The index should never equal 3 because any sum of index + 1 greater than 3 should result in a integer between 0 to 2 do to the modulus operator, %. I also forgot some semicolons. I just edited the code and it should would now when put inside your method.
– Andi
Jul 13, 2014 at 19:48