Algorithm for the fair distribution of numbers into two sets

Given a set of n numbers (1 <= n <= 100), where each number is an integer from 1 to 450, we need to distribute these numbers of numbers into two sets A and B, so that the following two cases are true:

  • The total number in each set differs by no more than 1.
  • The sum of all numbers in is as close as possible to the sum of all numbers in B, that is, the distribution must be fair.

Can someone suggest an efficient algorithm to solve the above problem?

Thanks.

+3
source share
13 answers

Since the numbers are small, it is not NP-complete.

To solve this problem, you can use dynamic programming:

true t [s, n, i] , s n i. t [s, n, i] t [s, n, i-1] t [s - a [i], n-1, i-1]. n/2, .

: . t_i [s, n] i-1, , . ( .)

+9

. 2 , . , : 2 N/2 ( 1 N/2 N/2 + 1, , ). , , .

+3

, A B.

, , ...

, ( ).

, .

+2

, , . " " , NP .


?

  • A
  • B
  • B
  • A
  • 1, .

:

A B , ,

(n) + (n - 3) == (n - 1) + (n - 2)

1 0, 1 1, 2 [1,2], 3 [1,2,3] .

0 , .

1 1 A. . , .

2 2 A 1 B. , A . , , .

3 3 A 2 1 B. (3 == 2 + 1), B .

+2

-, (.. ). DP ( DP , - - ).

, DP - , . N ^ 3.

+2

. .

, n Xn 1 <= n <= 100 1 <= Xn <= 450.

  • n < 3 ,

  • n > 2, ,

  • S ,
  • S (n - n% 2)/2 A,
  • , A. , S1 , A S1 , , .
  • 5. , < 2. 1. .

:

: n = 7 10, 75, 30, 45, 25, 15, 20

1:

n > 2, : 10, 15, 20, 25, 30, 45, 75

S = 220

A = 220/((7-1)/2) = 73

:

10 75 = > 85

15 45 = > 60

20 30 = > 50

< 2, 25 : 85 (10,75), 60 (15,45), 50 (20,30), 25 (25)

Pass 2:

n = 4, - 85, 60, 50, 25

> 2, : 25 (25), 50 (20,30), 60 (15,45), 85 (10,75)

S (S = 220), A : A = 220/((4-0)/2) = 110

:

25 85 = > 110

50 60 = > 110

: 110 (25 (25), 85 (10,75)), 110 (50 (20,30), 60 (15,45))

3:

n = 2, - 110, 110

n < 3 :

A = 25, 10, 75

B = 20, 30, 15, 45

, .

+2

№ 2 , : " B", , " " . ""? ?

+1

@ShreevatsaR , . ( 10 100 , , ).

. " " , , - 2002 ., ShreevatsaR.

#!/usr/bin/perl

use strict;
use warnings;

use List::Util qw( sum );

my @numbers = generate_list();

print "@numbers\n\n";

my (@A, @B);
my $N = @numbers;

while ( @numbers ) {
    my $n = pop @numbers;
    printf "Step: %d\n", $N - @numbers;
    {
        no warnings 'uninitialized';

        if ( sum(@A) < sum(@B) ) {
            push @A, $n;
        }
        else {
            push @B, $n;
        }
        printf "A: %s\n\tsum: %d\n\tnum elements: %d\n",
            "@A", sum(@A), scalar @A;
        printf "B: %s\n\tsum: %d\n\tnum elements: %d\n\n",
            "@B", sum(@B), scalar @B;
    }
}

sub generate_list { grep { rand > 0.8 } 1 .. 450 }

, generate_list .

+1

, , ?

- 1 . , , ( ). , , .

0

( ).

0

. 1. true, 2.

0

, . - , , , , - . , , .

#include <stdio.h>
#include <stdlib.h>

#define MAXPAR 50
#define MAXTRIES 10000000

int data1[] = {192,130,446,328,40,174,218,31,59,234,26,365,253,11,198,98,
               279,6,276,72,219,15,192,289,289,191,244,62,443,431,363,10
              } ;
int data2[] = { 1,2,3,4,5,6,7,8,9 } ;

// What does the set sum to
int sumSet ( int data[], int len )
{
    int result = 0 ;
    for ( int i=0; i < len; ++i )
        result += data[i] ;
    return result ;
}

// Print out a set
void printSet ( int data[], int len )
{
    for ( int i=0; i < len; ++i )
        printf ( "%d ", data[i] ) ;
    printf ( " Sums to %d\n", sumSet ( data,len ) ) ;
}

// Partition the values using simulated annealing
void partition ( int data[], size_t len )
{
    int set1[MAXPAR] = {0} ;    // Parttition 1
    int set2[MAXPAR] = {0} ;    // Parttition 2
    int set1Pos, set2Pos, dataPos, set1Len, set2Len ;  // Data about the partitions
    int minDiff ; // The best solution found so far
    int sum1, sum2, diff ;
    int tries = MAXTRIES ; // Don't loop for ever

    set1Len = set2Len = -1 ;
    dataPos = 0 ;

    // Initialize the two partitions
    while ( dataPos < len )
    {
        set1[++set1Len] = data[dataPos++] ;
        if ( dataPos < len )
            set2[++set2Len] = data[dataPos++] ;
    }


    // Very primitive simulated annealing solution
    sum1 = sumSet ( set1, set1Len ) ;
    sum2 = sumSet ( set2, set2Len ) ;
    diff = sum1 - sum2 ;    // The initial difference - we want to minimize this
    minDiff = sum1 + sum2 ;
    printf ( "Initial diff is %d\n", diff ) ;

    // Loop until a solution is found or all are tries are exhausted
    while ( diff != 0 && tries > 0 )
    {
        // Look for swaps that improves the difference
        int newDiff, newSum1, newSum2 ;
        set1Pos = rand() % set1Len ;
        set2Pos = rand() % set2Len ;

        newSum1 = sum1 - set1[set1Pos] + set2[set2Pos] ;
        newSum2 = sum2 + set1[set1Pos] - set2[set2Pos] ;
        newDiff = newSum1 - newSum2 ;
        if ( abs ( newDiff ) < abs ( diff ) ||      // Is this a better solution?
                tries/100 > rand() % MAXTRIES )     // Or shall we just swap anyway - chance of swap decreases as tries reduces
        {
            int tmp = set1[set1Pos] ;
            set1[set1Pos] = set2[set2Pos] ;
            set2[set2Pos] = tmp ;
            diff = newDiff ;
            sum1 = newSum1 ;
            sum2 = newSum2 ;

            // Print it out if its the best we have seen so far
            if ( abs ( diff ) < abs ( minDiff ) )
            {
                minDiff = diff ;
                printSet ( set1, set1Len ) ;
                printSet ( set2, set2Len ) ;
                printf ( "diff of %d\n\n", abs ( diff ) ) ;
            }
        }


        --tries ;
    }

    printf ( "done\n" ) ;
}


int main ( int argc, char **argv )
{
    // Change this to init rand from the clock say if you don't want the same
    // results repoduced evert time!
    srand ( 12345 ) ;
    partition ( data1, 31 ) ;
    partition ( data2, 9 ) ;
    return 0;
}
0

, .

- N, 0 1 . , , , ... - :

fitness(gen) = (sum(gen)-n/2))^2 + (sum(values[i]*(-1)**gen[i] for i in 0..n))^2

( )

Of course, this may give you a suboptimal answer, but for big problems in the real world this is usually enough.

-1
source

Source: https://habr.com/ru/post/1719063/


All Articles