1

Suppose I have an array of n numbers. The rules for the array is as below:

  1. All the numbers must add up to 1000
  2. If a number changes (positive or negative), all the other numbers in the array must be adjusted so that the sum of the array is still 1000
  3. The hard part - all numbers must be greater than or equal to 0.

Number 1 and 2 can be easily done by getting the delta of change, and spreading the delta among the other numbers. The trick is condition 3.

There's a question asking for its application. The numbers are percentages to be fed into pie chart; the user can adjust each pie segment with a slider. As it is a pie chart using percentages, all the sliders must add up to 100% - meaning if the user drag the slider to reduce the size of one pie slice, all the other pie slices should increase. No slice of the pie should be less than 0 or more than 100.

Extrakun
  • 979

2 Answers2

4

Basically, your range is 0 to 1000. When one value is changed, your new range is 0 to sum(N). You want to then normalize to 0 to 1000. So you compute the fraction of the total for each value and then multiple by 1000. Each value is thus multiplied by 1000/sum(N). Here's an example:

[100,100,300,500]

Now suppose we change one value:

[100,300,300,500] # sums to 1200

Now multiple each value by 1000/1200. (The range you want divided by the sum you have.)

[83, 250, 250, 417] # sums to 1000

Updated

As @AProgrammer points out, depending on how you round, you may be off by one. You probably want to use the floor as a result and then add one to entries until you reach the desired sum. Or alternatively, if the APIs that consume this can use floating point values, use those.

Gort the Robot
  • 14,774
  • 4
  • 51
  • 60
  • Honestly, reducing each slice by a fraction, instead of an outright subtraction, is something I have not thought of. - it also take care of the >= 0 requirement. – Extrakun Jan 08 '16 at 08:44
  • 1
    the first row [100,200,300,500] sums up to 1100, not 1000. – Pieter B Jan 08 '16 at 09:23
  • 2
    "Note that to make this work, you need to use traditional rounding rules, not integer math." It's more complex (consider [400, 400, 400] if you don't take the other values into account, you have a sum of 999 or of 1002). You need to truncate and then distribute the excess. – AProgrammer Jan 08 '16 at 10:05
  • You can be off by nearly .5 per number. – Deduplicator Jan 08 '16 at 21:47
2

As a programming problem, there are lots of easy answers to this; three quick brainstormed ones:

  • When you increase a slice, decrease the size of the last slice with a size greater than zero by an equivalent amount.
  • When you increase a slice, decrease the size of the largest slice by an equivalent amount.
  • When you increase a slice, decrease the size of all other slices in proportion to their current values. For example, if your slices where currently sized (10, 20, 30, 40) and you increased the 40 slice to 46, distribute this reduction among the other slices in the ratio 1:2:3 so you end up with (9, 18, 27, 46).

And fairly obvious equivalent algorithms for decreasing a slice.

However, this is probably more interesting as a UX problem - which algorithm produces the most "intuitive" behaviour? As such, it may be worth asking at User Experience.