0

I don't know it the correct name is "balance".

Well I have an array of 2n positive integer elements and I want to split into two n elements array, with the minimum difference between their average. For example:

values: {4, 4, 7, 8, 10, 15}
(some magic here)
a: {7, 8, 10}
b: {4, 4, 15}

I'm not sure if always combining the smallest number with the biggest one will split the less different average always. Is there any way to implement this algorithm always correctly splitting?

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
ranieri
  • 2,030
  • 2
  • 21
  • 39
  • 3
    This problem is NP-hard (because if you can verify that you have found the minimum difference between averages then you have also solved the [subset sum problem](http://en.wikipedia.org/wiki/Subset_sum_problem).) – finnw May 24 '13 at 09:36
  • 1
    @finnw: BTW, the OP's problem is usually called the [Partition Problem](https://en.wikipedia.org/wiki/Partition_problem) – hugomg May 24 '13 at 23:21

2 Answers2

0

If I understand your need correctly, one fast and approximately correct solution would be to:

  1. Sort the original array
  2. Allocate the new arrays to be each half as large as the original. If the original is odd length, decide which new array gets the extra element.
  3. Walk the original array from left to right (or right to left). Put the odd index elements in one of the new arrays and the even index elements in the other new array.

The key is sorting the original array, either in-place if allowed by your requirements, or create a new "original" array which is a sorted copy of the real original array if in-place sorting is not allowed.

This solution will be quite close to the ideal solution if N is fairly large and the distribution of numbers is fairly uniform (e.g. no large skews toward one end or the other).

If you really require the averages to be as close as possible to each other, even if there are large deviations in the numbers, the only approach I can think of is to try every permutation and keep the one with the smallest difference in average.

Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • The quantity is never odd, as I said the length of one array is 2n and I want to split into two n-length array. – ranieri May 24 '13 at 02:16
0
local function some_magic(V)
   assert(#V % 2 == 0 and #V > 0)
   table.sort(V)
   local S = {[-1] = math.huge, [0] = 0}
   for i = 1, #V do
      S[i] = S[i-1] + V[i]
   end
   local half_sum = math.floor(S[#V] / 2)
   local half_len = #V / 2
   local m = 2^math.ceil(math.log(half_len+1)/math.log(2))
   local P = {[0] = 0}
   for idx = #V, 1, -1 do
      local v, P2 = V[idx], {}
      for k in pairs(P) do
         if math.floor(k/m) + v + S[half_len - k%m - 1] <= half_sum then
            P2[k + v*m + 1] = idx
         end
      end
      for k, v in pairs(P2) do
         P[k] = P[k] or v
      end
   end
   local k = 0
   for next_k in pairs(P) do
      if next_k > k and next_k%m == half_len then
         k = next_k
      end
   end
   local A, B, prev_idx = {}, {}, 0
   repeat
      local idx = P[k]
      for i = prev_idx + 1, idx - 1 do
         table.insert(B, V[i])
      end
      table.insert(A, V[idx])
      prev_idx = idx
      k = k - V[idx]*m - 1
   until k == 0
   for i = prev_idx + 1, #V do
      table.insert(B, V[i])
   end
   return A, B
end

local values = {4, 4, 7, 8, 10, 15}
print('values: '..table.concat(values, ','))
local a, b = some_magic(values)
print('a: '..table.concat(a, ','))
print('b: '..table.concat(b, ','))

------------- Output: ------------
values: 4,4,7,8,10,15
a: 4,4,15
b: 7,8,10
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64