Algorithm to split an array into P subarrays of balanced sum

前端 未结 10 1733
面向向阳花
面向向阳花 2020-12-08 05:00

I have an big array of length N, let\'s say something like:

2 4 6 7 6 3 3 3 4 3 4 4 4 3 3 1

I need to split this array into P subarrays (in

相关标签:
10条回答
  • 2020-12-08 05:35

    You can use Max Flow algorithm.

    0 讨论(0)
  • 2020-12-08 05:40

    I'm wondering whether the following would work:

    Go from the left, as soon as sum > sigma, branch into two, one including the value that pushes it over, and one that doesn't. Recursively process data to the right with rightSum = totalSum-leftSum and rightP = P-1.

    So, at the start, sum = 60

    2 4 6 7 6 3 3 3 4 3 4 4 4 3 3 1
    

    Then for 2 4 6 7, sum = 19 > sigma, so split into:

    2 4 6     7 6 3 3 3 4 3 4 4 4 3 3 1
    
    2 4 6 7     6 3 3 3 4 3 4 4 4 3 3 1
    

    Then we process 7 6 3 3 3 4 3 4 4 4 3 3 1 and 6 3 3 3 4 3 4 4 4 3 3 1 with P = 4-1 and sum = 60-12 and sum = 60-19 respectively.

    This results in, I think, O(P*n).

    It might be a problem when 1 or 2 values is by far the largest, but, for any value >= sigma, we can probably just put that in it's own partition (preprocessing the array to find these might be the best idea (and reduce sum appropriately)).

    If it works, it should hopefully minimise sum-of-squared-error (or close to that), which seems like the desired measure.

    0 讨论(0)
  • 2020-12-08 05:43

    Working code below (I used php language). This code decides part quantity itself;

    $main = array(2,4,6,1,6,3,2,3,4,3,4,1,4,7,3,1,2,1,3,4,1,7,2,4,1,2,3,1,1,1,1,4,5,7,8,9,8,0);
    $pa=0;
    for($i=0;$i < count($main); $i++){
    $p[]= $main[$i];
    if(abs(15 - array_sum($p)) < abs(15 - (array_sum($p)+$main[$i+1])))
    {
    $pa=$pa+1;
    $pi[] = $i+1;
    $pc =  count($pi);
    
    $ba = $pi[$pc-2] ;
    
    $part[$pa] = array_slice( $main,  $ba, count($p));
    unset($p);
    }
    }
    print_r($part);
    for($s=1;$s<count($part);$s++){
    echo '<br>';
    echo array_sum($part[$s]);
    }
    

    code will output part sums like as below

    13
    14
    16
    14
    15
    15
    17
    
    0 讨论(0)
  • 2020-12-08 05:44

    This is very similar to the case of the one-dimensional bin packing problem, see http://www.cs.sunysb.edu/~algorith/files/bin-packing.shtml. In the associated book, The Algorithm Design Manual, Skienna suggests a first-fit decreasing approach. I.e. figure out your bin size (mean = sum / N), and then allocate the largest remaining object into the first bin that has room for it. You either get to a point where you have to start over-filling a bin, or if you're lucky you get a perfect fit. As Skiena states "First-fit decreasing has an intuitive appeal to it, for we pack the bulky objects first and hope that little objects can fill up the cracks."

    As a previous poster said, the problem looks like it's NP-complete, so you're not going to solve it perfectly in reasonable time, and you need to look for heuristics.

    0 讨论(0)
  • 2020-12-08 05:46

    I recently needed this and did as follows;

    1. create an initial sub-arrays array of length given sub arrays count. sub arrays should have a sum property too. ie [[sum:0],[sum:0]...[sum:0]]
    2. sort the main array descending.
    3. search for the sub-array with the smallest sum and insert one item from main array and increment the sub arrays sum property by the inserted item's value.
    4. repeat item 3 up until the end of main array is reached.
    5. return the initial array.

    This is the code in JS.

    function groupTasks(tasks,groupCount){
      var  sum = tasks.reduce((p,c) => p+c),
       initial = [...Array(groupCount)].map(sa => (sa = [], sa.sum = 0, sa));
      return tasks.sort((a,b) => b-a)
                  .reduce((groups,task) => { var group = groups.reduce((p,c) => p.sum < c.sum ? p : c);
                                             group.push(task);
                                             group.sum += task;
                                             return groups;
                                           },initial);
    }
    
    var tasks = [...Array(50)].map(_ => ~~(Math.random()*10)+1), // create an array of 100 random elements among 1 to 10
       result = groupTasks(tasks,7);                             // distribute them into 10 sub arrays with closest sums
    
    console.log("input array:", JSON.stringify(tasks));
    console.log(result.map(r=> [JSON.stringify(r),"sum: " + r.sum]));

    0 讨论(0)
  • 2020-12-08 05:47

    If I am not mistaken here, one more approach is dynamic programming.

    You can define P[ pos, n ] as the smallest possible "penalty" accumulated up to position pos if n subarrays were created. Obviously there is some position pos' such that

    P[pos', n-1] + penalty(pos', pos) = P[pos, n]

    You can just minimize over pos' = 1..pos.

    The naive implementation will run in O(N^2 * M), where N - size of the original array and M - number of divisions.

    0 讨论(0)
提交回复
热议问题