Legendary
Legendary

Reputation: 55

Algorithms: To determine whether a given set has two subsets which are disjoint such that sum of elements in both subsets is same?

Here is the code that I have written:-

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

int ctr = 0;

void partition(int arr[], int n) {
    int i, j, k = 0, r, in = 0, te, flag = 0;
    int *sum;
    sum = (int*)malloc(sizeof(int) * pow(2, n));
    for (i = 0; i < pow(2, n); i++) {
        printf("{");
        for (j = 0; j < n; j++) {
            if (i & (1 << j)) {
                printf("%d ", arr[j]);
                sum[k] = sum[k] + arr[j];
            }
        }
        k++;
        printf("}");
        printf("\n");
    }
    printf("\n \n");

    int *temp;
    for (k = 0; k < pow(2, n) - 1; k++) {
        in = 0;
        temp = (int*)malloc(sizeof(int));
        for (r = k + 1; r < pow(2, n); r++) {
            if (sum[k] == sum[r]) {
                printf("\n Printing solution for sum %d", sum[k]);
                for (i = 0; i < pow(2, n); i++) {
                    if (i == k || i == r) {
                        printf("{");
                        for (j = 0; j < n; j++) {
                            if (i & 1 << j) {
                                for (te = 0; te < in; te++) { //Disjointness
                                    if (temp[te] == arr[j])
                                        flag = 1;
                                }
                                if (flag == 1)
                                    break;
                                temp[in++] = arr[j];
                                temp = (int*)realloc(temp, sizeof(int));
                                printf("%d ", arr[j]);
                            }
                        }
                        printf("}");
                        printf("\n");
                    }
                }
                break;          
            }
        }
        free(temp);
    }
}

void main() {
    int *arr, n, i;
    printf("\n Enter the number of elements in the set \n");
    scanf("%d", &n);
    arr = (int*)malloc(sizeof(int) * n);
    printf("\n Enter the set elements \n");
    for (i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    partition(arr, n);
}

It works perfectly for the input {1,2,3}. For {1,2,3,4}, it gives the correct output as well.

For {1,2,3,4}, the output is:

Printing solution for sum 3{1 2 }
{3 }

 Printing solution for sum 4{1 3 }
{4 }

 Printing solution for sum 5{2 3 }
{1 4 }

 Printing solution for sum 6{1 2 3 }
{}

 Printing solution for sum 7{}
{}

But if the input is {1,5,11,5}, the output is:

 Printing solution for sum 5{5 }
{}

 Printing solution for sum 6{}
{}

 Printing solution for sum 11{}
{}

 Printing solution for sum 16{}
{}

 Printing solution for sum 17{}
{}

Since {1,5,5} and {11} is a solution, in this case, the expected output should be Printing Solution for sum 11 {1,5,5} and {11} but it's not displaying that.

What is the problem and how can I fix it?

Upvotes: 4

Views: 515

Answers (2)

chqrlie
chqrlie

Reputation: 144780

Your algorithm is too complicated. You start on the correct path, computing the sum of all subsets of the set, but you do not correctly compute all possible disjoint subsets to try and find one with the same sum.

Here is a simpler approach:

  • enumerate all subsets
  • for each subset, compute the sum and store a structure with the subset signature and its sum into an array.
  • sort the array by the value of the sum
  • iterate over the sorted array:
  • for each element, iterate over the subsequent elements with the same sum, if one of them has a disjoint subset signature, you have a match, print it.
  • the space complexity is O(2N) and the time complexity is even worse at O(2N+1), which is very bad, but manageable for small sets.

Here is a modified version:

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

struct s { int sig, sum; };

int compare(const void *p1, const void *p2) {
    int sum1 = ((const struct s*)p1)->sum;
    int sum2 = ((const struct s*)p2)->sum;
    return (sum1 > sum2) - (sum1 < sum2);
}

void print_set(int arr[], size_t sig) {
    printf("{");
    while (sig) {
        if (sig & 1)
            printf(" %d", *arr);
        arr++;
        sig >>= 1;
    }
    printf(" } ");
}

void partition(int arr[], int n) {
    size_t i, j, count = 1ULL << n;
    struct s *array = calloc(sizeof(*array), count);
    int b, sum;

    if (array != NULL) {
        for (i = 1; i < count; i++) {
            sum = 0;
            for (b = 0; b < n; b++) {
                if (i & (1ULL << b))
                    sum += arr[b];
            }
            array[i].sig = i;
            array[i].sum = sum;
        }
        qsort(array, count, sizeof(*array), compare);
        for (i = 0; i < count; i++) {
            for (j = i + 1; j < count && array[i].sum == array[j].sum; j++) {
                if ((array[i].sig & array[j].sig) == 0) {
                    printf("solution with sum=%d: ", array[i].sum);
                    print_set(arr, array[i].sig);
                    print_set(arr, array[j].sig);
                    printf("\n");
                }
            }
        }
        free(array);
    }
}

int main() {
    int *arr, n, i;
    printf("Enter the number of elements in the set: ");
    if (scanf("%d", &n) == 1) {
        arr = (int*)malloc(sizeof(int) * n);
        if (arr != NULL) {
            printf("Enter the set elements: ");
            for (i = 0; i < n; i++) {
                if (scanf("%d", &arr[i]) != 1)
                    return 1;
            }
            partition(arr, n);
            free(arr);
        }
    }
    return 0;
}

Output:

Enter the number of elements in the set: 3
Enter the set elements:  1 2 3
solution with sum=3: { 3 } { 1 2 } 

Enter the number of elements in the set: 4
Enter the set elements: 1 2 3 4
solution with sum=3: { 1 2 } { 3 }
solution with sum=4: { 4 } { 1 3 }
solution with sum=5: { 2 3 } { 1 4 }

Enter the number of elements in the set: 4
Enter the set elements: 1 5 11 5
solution with sum=5: { 5 } { 5 }
solution with sum=11: { 11 } { 1 5 5 }

Upvotes: 1

Richard Chambers
Richard Chambers

Reputation: 17593

You may find the following to be a place to start. What this sample program does is to take a set of values and then generate as possible disjoint subsets where the sum of the elements of each subset are equal between the two subsets.

I am not sure if this actually answers your question. The requirements specified are quite loose. The source you provide does not compile with Visual Studio and when a few basic compile errors were corrected the resulting program crashed. I also find your example output confusing.

The criteria for disjoint subsets as created by this program are:

  • values in one subset can not appear in the other
  • membership is ordered and tracked via an index assigned to each element of the superset
  • a subset may have duplicate values but not duplicate indexed elements
  • the empty set is not considered to be a valid disjoint subset since it has no elements
  • there must be at least two subsets that can be formed from the set
  • only two subsets are considered and there may be more (e.g. {1, 2, 3, 4, 5} could have {2,3}, {1,4}, {5}).

The source code follows. This is from a single C source code file that is part of a Visual Studio C++ solution with the C++ main() calling the C source code entry point of mymain2(). I did it that way because it was easier for me.

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

typedef struct {
    int *pSet;        // array of values. array is malloced when max number is known.
    int  nSetMax;     // max number of array elements, amount malloced.
    int  nSetCur;     // current number of in use array elements.
} SetType;

static void generateSet (int i, int arr[], int n, SetType *temp)
{
    int j;

    temp->nSetCur = 0;
    for (j = 0; j < n; j++)
    {
        if ( i & (1 << j))
        {
            int te;
            int flag = 0;

            for (te = 0; te < temp->nSetCur; te++)
            {
                // we compute disjoint property by the order of the elements in
                // the set using element index in order to allow duplicate values per
                // request of the question. Exampe 1, 5, 11, 5 as list of values.
                if (temp->pSet[te] == j)
                    flag = 1;
            }
            if (flag == 1)
                continue;
            temp->pSet[temp->nSetCur] = j;
            temp->nSetCur++;
        }
    }
}

static void printItems (int arr[], const SetType temp)
{
    if (temp.nSetCur > 0) {
        int j;
        int sum = 0;

        printf("    {");
        for (j = 0; j < temp.nSetCur; j++) {
            printf("%2d ", arr[temp.pSet[j]]);
            sum += arr[temp.pSet[j]];
        }
        printf("} = %3d\n", sum);
    } else
        printf ("    {}\n");
}

// determine if the two sets are disjoint by comparing the element indexes
// of the elements in each subset. the element index is the position of the
// subset element in the set from which the subset was drawn.
static int checkSetsByIndex (const SetType tempI, const SetType tempR)
{
    int i;

    for (i = 0; i < tempI.nSetCur; i++) {
        int j;
        for (j = 0; j < tempR.nSetCur; j++) {
            if (tempI.pSet[i] == tempR.pSet[j])
                return 0;
        }
    }

    return 1;
}

// determine if the two sets are disjoint by comparing the element values
// of the elements in each subset. the element value is the value of the
// subset element in the set from which the subset was drawn.
// we may have duplicate values but as long as they are not in two different
// subsets then we allow it.
static int checkSetsByValue (int arr[], const SetType tempI, const SetType tempR)
{
    int i;

#if 1
    // following code does a check that the two sets are disjoint by value
    // meaning they do not share any values. however a set may have multiple
    // elements with the same value.
    // if not needed then you can turn it off with Preprocessor check.
    for (i = 0; i < tempI.nSetCur; i++) {
        int j;
        for (j = 0; j < tempR.nSetCur; j++) {
            if (arr[tempI.pSet[i]] == arr[tempR.pSet[j]])
                return 0;
        }
    }
#endif

    return 1;
}

static void partition(int arr[], int n)
{
    int i;
    int iPow2n = pow(2, n);
    int *sum = calloc(iPow2n, sizeof(int));

    printf ("Generate and list sums\n");
    for(i = 0; i < iPow2n; i++)
    {
        int j;
        printf("  {");
        for (j = 0; j < n; j++)
        {
            if (i & (1 << j))
            {
                printf("%2d ", arr[j]);
                sum[i] = sum[i] + arr[j];
            }
        }
        printf("} = %3d\n", sum[i]);
    }

    printf("\n\nGenerate list of disjoint sets for each sum.\n");

    for (i = 0; i < iPow2n - 1; i++)
    {
        int r;
        SetType tempI = {calloc(iPow2n, sizeof(SetType)), iPow2n, 0};
        SetType tempR = {calloc(iPow2n, sizeof(SetType)), iPow2n, 0};

        for(r = i + 1; r < iPow2n; r++)
        {
            if(sum[i] == sum[r])
            {
                generateSet (i, arr, n, &tempI);
                generateSet (r, arr, n, &tempR);
                // check disjoint subsets by looking at the index of the
                // subset elements where the index is the index of the original
                // set from which the subset is drawn.
                if (checkSetsByIndex (tempI, tempR)) {
                    // check that the two subsets are disjoint by element values
                    // as well. this means that while a subset may have duplicate
                    // values, the elements of each subset must be disjoint can not
                    // have elements whose values are the same.
                    // so if we have a set {1, 5, 11, 5} then subsets of
                    // {5}, {5} is invalid but {1, 5, 5}, {11} is valid.
                    if (checkSetsByValue (arr, tempI, tempR)) {
                        printf("\n Printing solution for sum %d\n", sum[i]);
                        printItems (arr, tempI);
                        printItems (arr, tempR);
                        break;
                    }
                }
            }
        }
        free(tempI.pSet);
        free(tempR.pSet);
    }
}

int mymain2(void)
{
    int n;
    printf("\n Enter the number of elements in the set \n");
    scanf("%d",&n);

    if (n > 0) {
        int i;
        int *arr = malloc(sizeof(int) * n);
        printf("\n Enter the set elements \n");
        for (i = 0; i < n; i++)
        {
            scanf("%d",&arr[i]);
        }
        partition(arr,n);

        free (arr);
    }

    return 0;
}

For a set of 4 values whose elements are {1, 2, 3, 4} it generates the following output:

 Enter the number of elements in the set
4

 Enter the set elements
1 2 3 4
Generate and list sums
  {} =   0
  { 1 } =   1
  { 2 } =   2
  { 1  2 } =   3
  { 3 } =   3
  { 1  3 } =   4
  { 2  3 } =   5
  { 1  2  3 } =   6
  { 4 } =   4
  { 1  4 } =   5
  { 2  4 } =   6
  { 1  2  4 } =   7
  { 3  4 } =   7
  { 1  3  4 } =   8
  { 2  3  4 } =   9
  { 1  2  3  4 } =  10


Generate list of disjoint sets for each sum.

 Printing solution for sum 3
    { 1  2 } =   3
    { 3 } =   3

 Printing solution for sum 4
    { 1  3 } =   4
    { 4 } =   4

 Printing solution for sum 5
    { 2  3 } =   5
    { 1  4 } =   5

For a set of 4 values {1, 5, 11, 5} it generates the following output:

 Enter the number of elements in the set
4

 Enter the set elements
1 5 11 5
Generate and list sums
  {} =   0
  { 1 } =   1
  { 5 } =   5
  { 1  5 } =   6
  {11 } =  11
  { 1 11 } =  12
  { 5 11 } =  16
  { 1  5 11 } =  17
  { 5 } =   5
  { 1  5 } =   6
  { 5  5 } =  10
  { 1  5  5 } =  11
  {11  5 } =  16
  { 1 11  5 } =  17
  { 5 11  5 } =  21
  { 1  5 11  5 } =  22


Generate list of disjoint sets for each sum.

 Printing solution for sum 11
    {11 } =  11
    { 1  5  5 } =  11

For a set of 5 values {1, 2, 3, 4, 5} it generates the following output:

 Enter the number of elements in the set
5

 Enter the set elements
1 2 3 4 5
Generate and list sums
  {} =   0
  { 1 } =   1
  { 2 } =   2
  { 1  2 } =   3
  { 3 } =   3
  { 1  3 } =   4
  { 2  3 } =   5
  { 1  2  3 } =   6
  { 4 } =   4
  { 1  4 } =   5
  { 2  4 } =   6
  { 1  2  4 } =   7
  { 3  4 } =   7
  { 1  3  4 } =   8
  { 2  3  4 } =   9
  { 1  2  3  4 } =  10
  { 5 } =   5
  { 1  5 } =   6
  { 2  5 } =   7
  { 1  2  5 } =   8
  { 3  5 } =   8
  { 1  3  5 } =   9
  { 2  3  5 } =  10
  { 1  2  3  5 } =  11
  { 4  5 } =   9
  { 1  4  5 } =  10
  { 2  4  5 } =  11
  { 1  2  4  5 } =  12
  { 3  4  5 } =  12
  { 1  3  4  5 } =  13
  { 2  3  4  5 } =  14
  { 1  2  3  4  5 } =  15


Generate list of disjoint sets for each sum.

 Printing solution for sum 3
    { 1  2 } =   3
    { 3 } =   3

 Printing solution for sum 4
    { 1  3 } =   4
    { 4 } =   4

 Printing solution for sum 5
    { 2  3 } =   5
    { 1  4 } =   5

 Printing solution for sum 5
    { 1  4 } =   5
    { 5 } =   5

 Printing solution for sum 6
    { 2  4 } =   6
    { 1  5 } =   6

 Printing solution for sum 7
    { 3  4 } =   7
    { 2  5 } =   7

Upvotes: 1

Related Questions