Introduction

This was a task at a university project, where I was the only one to solve it with a time complexity of O(N).

This is not the known Partition Problem that has been proven to be NP-Hard.

Objective

Problem

We are given an initial set set_a, which contains N natural numbers, and the task is to partition this set into two subsets, set_b and set_c, while following a specific set of rules:

  1. Every element in set_b must be smaller than every element in set_c.
  2. The sum of all elements in set_b should be equal to the sum of all elements in set_c if possible.
  3. If achieving equal sums is not possible, the sum of set_b should be greater than the sum of set_c.
  4. If set_a contains only the element 0, then set_b should contain 0 and set_c should be empty.

The key objective is to solve this problem with a time complexity of O(N), where N is the number of elements in set_a.

Assumptions

Algorithm

void set_partition (Set * b, Set * c, Set a) {
    unsigned int sum_b = 0; unsigned int sum_c = 0; unsigned int max = 0;
    
    // Condition to prevent the case of set_a being empty
    if (set_max(&max, a) == 1) {
        int down = 0;
        int number = -1;

        // Time Complexity: O(N) : N = Size of the Set
        // Iterate through all elements
        for (int i = max - 1; i >= 0; i--) {

            // Decide what element to take
            // If sum_b is smaller than sum_c + max keep adding elements to sum_b
            // considering the second rule of the objective
            number = sum_b < sum_c + max ? down++ : down + i;
            
            // Because of implementation details of the set-type, 
            // it needs to be checked if the set contains the current number
            if (set_contains(a, number)) {
                if (number > down) {
                    // inserting the current highest number
                    set_insert(c, max);
                    sum_c += max;
                    max = number;
                } else {
                    // inserting in ascending order
                    set_insert(b, number);
                    sum_b += number;
                }
            }
        }
        // Last step, insert the last found maximum
        set_insert((sum_b >= sum_c + max && max != 0) ? c : b, max);
    }
}

Explanation

Here’s how the algorithm works:

Step 1: Initial Setup

If set_a contains only 0, the algorithm directly inserts 0 into set_b and leaves set_c empty, satisfying the fourth rule of the problem.

Step 2: Iterating Through the Set

The core of the algorithm is a loop that starts from the second-largest element (i.e., max - 1) and works its way down to the smallest element. The goal is to decide which subset (set_b or set_c) each element should be added to, while maintaining the sum conditions.

During each iteration, the algorithm checks whether the sum of set_b is smaller than the sum of set_c plus the current maximum element (sum_c + max). Based on this comparison, it assigns the next smallest element (starting from the smallest) to set_b or set_c as follows:

Step 3: Inserting Elements from Top and Bottom

Step 4: Final Insertion of the Maximum Element

Once the loop completes, the algorithm performs one final check: it decides which subset the maximum element should be inserted into, based on the sums of set_b and set_c.

This final step ensures that the partitioning adheres to the rules outlined earlier.

ELI5

We have the initial set_a that is an ordered set with all our elements. We start from the maximum value and keep it mind.

Now we start iterating through all the other numbers, we can decide for each iteration whether to take from the smallest or highest number of the remaining elements.

We keep inserting our elements in each iteration into set_b until it is larger than set_c + maximum. Once this happens we know that the maximum can now be inserted into set_c, because it won't break any of the rules.