We have:
var range = [9,18,3,14,2,6,12,7,11,2,1,4]
var total = 89;
var group_size = total / 3;
As result I need 3 groups similar sized but group1 and group2 can never be bigger than group_size.
The result for this example would be
var group1 = 27; // 9,18
var group2 = 25; // 3,14,2,6
var group3 = 37; // 12,7,11,2,1,4
How can I achieve this in Javascript/jQuery?
I think you need to pop values with Javascript if you want this. Something like:
Update: This really answers a similar question (since removed by the author) which didn't include the restriction that the first two groups could not exceed the mean. With that restriction, it's a much simpler problem, and one that probably wouldn't have caught my attention. I'm leaving my answer here as the question it answers seems to be algorithmically interesting.
I have an answer using the Ramda functional programming library. You can see it in action on JSFiddle. (See below for an updated version of this that doesn't depend upon Ramda.) Ramda offers a number of convenient functions that make the code simpler. None of them should be surprising if you're at all used to functional programming, although if you're used to tools like Underscore or LoDash the parameter orders might seem backwards. (Believe me, there is a good reason.)
Here is some sample output from the Fiddle:
I am not at all convinced that this algorithm will give you an actual optimal solution. I think there is some chance that a search might fall into a local minimum and not notice the global minimum in a nearby valley of the search space. But I haven't tried very hard to either prove it or come up with a counterexample. There is also a reasonable chance that it actually is correct. I have no idea if there is some more efficient algorithm than this. The problem feels vaguely like partitioning problems and knapsack problems, but I know I've never run across it before. Many of those problems are NP Hard/NP Complete, so I wouldn't expect this to have a really efficient algorithm available.
This one works in a fairly simple recursive manner. The internal
val
function accepts an array of numbers, the number of groups to create, and and accumulator (soFar
) containing all the groups that have been created so far. Ifcount
is zero, it returns a simple result based upon the accumulator. Ifcount
is one, it recurs with an emptygroup
, acount
of zero, a an accumulator that now includes the originalgroup
as its last element.For any other value of
count
it calculates the mean size of the remaining group, and then chooses the last initial partial sum of the group smaller than the mean and the first one larger than it (with a special case if there is one exactly equal to it), recurs to finds the value if those partial sequences are used as groups in the anser, and then returns the one with the smaller value.Values are determined by calculated the variance in the total values of each subgroup formed. The variance is the sum of the squares of the distance from the mean.
For instance, if you started with these values:
And wanted to break them into three groups, you would have a mean of
If you broke them up like this:
you would have totals
and a variance of
But if you broke them like this:
your totals would be
and your variance would be
And since the latter split has the lower variance it is considered better.
Example
There is almost certainly plenty that could be done to clean up this code. But perhaps it's at least a start on your problem. It's been a very interesting challenge.
Update
I did create a version which does not depend upon Ramda. The code is very similar. (I guess I really didn't need Ramda, at least not for the final version.):
Of course, as now noted at the top, this answers a somewhat different question than is now being asked, but I think it's a more interesting question! :-)