How to divide a set into two subsets such that difference between the sum of numbers in two sets is minimal?

前端 未结 18 650
攒了一身酷
攒了一身酷 2020-11-27 10:55

Given a set of numbers, divide the numbers into two subsets such that difference between the sum of numbers in two subsets is minimal.

T

相关标签:
18条回答
  • 2020-11-27 11:38

    This can be solve using BST.
    First sort the array say arr1
    To start create another arr2 with the last element of arr1 (remove this ele from arr1)

    Now:Repeat the steps till no swap happens.

    1. Check arr1 for an element which can be moved to arr2 using BST such that the diff is less MIN diff found till now.
    2. if we find an element move this element to arr2 and go to step1 again.
    3. if we don't find any element in above steps do steps 1 & 2 for arr2 & arr1. i.e. now check if we have any element in arr2 which can be moved to arr1
    4. continue steps 1-4 till we don't need any swap..
    5. we get the solution.

    Sample Java Code:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * Divide an array so that the difference between these 2 is min
     * 
     * @author shaikhjamir
     *
     */
    public class DivideArrayForMinDiff {
    
        /**
         * Create 2 arrays and try to find the element from 2nd one so that diff is
         * min than the current one
         */
    
        private static int sum(List<Integer> arr) {
    
            int total = 0;
            for (int i = 0; i < arr.size(); i++) {
                total += arr.get(i);
            }
    
            return total;
        }
    
        private static int diff(ArrayList<Integer> arr, ArrayList<Integer> arr2) {
            int diff = sum(arr) - sum(arr2);
            if (diff < 0)
                diff = diff * -1;
            return diff;
        }
    
        private static int MIN = Integer.MAX_VALUE;
    
        private static int binarySearch(int low, int high, ArrayList<Integer> arr1, int arr2sum) {
    
            if (low > high || low < 0)
                return -1;
    
            int mid = (low + high) / 2;
            int midVal = arr1.get(mid);
    
            int sum1 = sum(arr1);
            int resultOfMoveOrg = (sum1 - midVal) - (arr2sum + midVal);
            int resultOfMove = (sum1 - midVal) - (arr2sum + midVal);
            if (resultOfMove < 0)
                resultOfMove = resultOfMove * -1;
    
            if (resultOfMove < MIN) {
                // lets do the swap
                return mid;
            }
    
            // this is positive number greater than min
            // which mean we should move left
            if (resultOfMoveOrg < 0) {
    
                // 1,10, 19 ==> 30
                // 100
                // 20, 110 = -90
                // 29, 111 = -83
                return binarySearch(low, mid - 1, arr1, arr2sum);
            } else {
    
                // resultOfMoveOrg > 0
                // 1,5,10, 15, 19, 20 => 70
                // 21
                // For 10
                // 60, 31 it will be 29
                // now if we move 1
                // 71, 22 ==> 49
                // but now if we move 20
                // 50, 41 ==> 9
                return binarySearch(mid + 1, high, arr1, arr2sum);
            }
        }
    
        private static int findMin(ArrayList<Integer> arr1) {
    
            ArrayList<Integer> list2 = new ArrayList<>(arr1.subList(arr1.size() - 1, arr1.size()));
            arr1.remove(arr1.size() - 1);
            while (true) {
    
                int index = binarySearch(0, arr1.size(), arr1, sum(list2));
                if (index != -1) {
                    int val = arr1.get(index);
                    arr1.remove(index);
                    list2.add(val);
                    Collections.sort(list2);
                    MIN = diff(arr1, list2);
                } else {
                    // now try for arr2
                    int index2 = binarySearch(0, list2.size(), list2, sum(arr1));
                    if (index2 != -1) {
    
                        int val = list2.get(index2);
                        list2.remove(index2);
                        arr1.add(val);
                        Collections.sort(arr1);
    
                        MIN = diff(arr1, list2);
                    } else {
                        // no switch in both the cases
                        break;
                    }
                }
            }
    
            System.out.println("MIN==>" + MIN);
            System.out.println("arr1==>" + arr1 + ":" + sum(arr1));
            System.out.println("list2==>" + list2 + ":" + sum(list2));
            return 0;
        }
    
        public static void main(String args[]) {
    
            ArrayList<Integer> org = new ArrayList<>();
            org = new ArrayList<>();
            org.add(1);
            org.add(2);
            org.add(3);
            org.add(7);
            org.add(8);
            org.add(10);
    
            findMin(org);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:39

    The recursive approach is to generate all possible sums from all the values of array and to check which solution is the most optimal one. To generate sums we either include the i’th item in set 1 or don’t include, i.e., include in set 2.

    The time complexity is O(n*sum) for both time and space.T

    public class MinimumSubsetSum {
    
      static int dp[][];
      public static int minDiffSubsets(int arr[], int i, int calculatedSum, int totalSum) {
    
        if(dp[i][calculatedSum] != -1) return dp[i][calculatedSum];
    
        /**
         * If i=0, then the sum of one subset has been calculated as we have reached the last
         * element. The sum of another subset is totalSum - calculated sum. We need to return the
         * difference between them.
         */
        if(i == 0) {
          return Math.abs((totalSum - calculatedSum) - calculatedSum);
        }
    
        //Including the ith element
        int iElementIncluded = minDiffSubsets(arr, i-1, arr[i-1] + calculatedSum,
            totalSum);
    
        //Excluding the ith element
        int iElementExcluded = minDiffSubsets(arr, i-1, calculatedSum, totalSum);
    
        int res = Math.min(iElementIncluded, iElementExcluded);
        dp[i][calculatedSum] = res;
        return res;
      }
    
      public static void util(int arr[]) {
        int totalSum = 0;
        int n = arr.length;
        for(Integer e : arr) totalSum += e;
        dp = new int[n+1][totalSum+1];
        for(int i=0; i <= n; i++)
          for(int j=0; j <= totalSum; j++)
            dp[i][j] = -1;
    
        int res = minDiffSubsets(arr, n, 0, totalSum);
        System.out.println("The min difference between two subset is " + res);
      }
    
    
      public static void main(String[] args) {
        util(new int[]{3, 1, 4, 2, 2, 1});
      }
    
    }
    
    0 讨论(0)
  • 2020-11-27 11:40
    #include<bits/stdc++.h>
    using namespace std;
    bool ison(int i,int x)
    {
     if((i>>x) & 1)return true;
     return false;
    }
    int main()
    {
    // cout<<"enter the number of elements  : ";
        int n;
        cin>>n;
        int a[n];
        for(int i=0;i<n;i++)
        cin>>a[i];
        int sumarr1[(1<<n)-1];
        int sumarr2[(1<<n)-1];
        memset(sumarr1,0,sizeof(sumarr1));
        memset(sumarr2,0,sizeof(sumarr2));
        int index=0;
        vector<int>v1[(1<<n)-1];
        vector<int>v2[(1<<n)-1];
    
        for(int i=1;i<(1<<n);i++)
        {  
           for(int j=0;j<n;j++)
           {
              if(ison(i,j))
              {
                 sumarr1[index]+=a[j];
                 v1[index].push_back(a[j]);
              }
              else
              {
                 sumarr2[index]+=a[j];
                 v2[index].push_back(a[j]);
              }
           }index++;
        }
        int ans=INT_MAX;
        int ii;
        for(int i=0;i<index;i++)
        {
           if(abs(sumarr1[i]-sumarr2[i])<ans)
           {
              ii=i;
              ans=abs(sumarr1[i]-sumarr2[i]);
           }
        }
        cout<<"first partitioned array : ";
        for(int i=0;i<v1[ii].size();i++)
        {
           cout<<v1[ii][i]<<" ";
        }
        cout<<endl;
        cout<<"2nd partitioned array : ";
        for(int i=0;i<v2[ii].size();i++)
        {
           cout<<v2[ii][i]<<" ";
        }
        cout<<endl;
        cout<<"minimum difference is : "<<ans<<endl;
    }
    
    0 讨论(0)
  • 2020-11-27 11:43

    Combinations over combinations approach:

    import itertools as it
    
    def min_diff_sets(data):
        """
            Parameters:
            - `data`: input list.
            Return:
            - min diff between sum of numbers in two sets
        """
    
        if len(data) == 1:
            return data[0]
        s = sum(data)
        # `a` is list of all possible combinations of all possible lengths (from 1
        # to len(data) )
        a = []
        for i in range(1, len(data)):
            a.extend(list(it.combinations(data, i)))
        # `b` is list of all possible pairs (combinations) of all elements from `a`
        b = it.combinations(a, 2)
        # `c` is going to be final correct list of combinations.
        # Let's apply 2 filters:
        # 1. leave only pairs where: sum of all elements == sum(data)
        # 2. leave only pairs where: flat list from pairs == data
        c = filter(lambda x: sum(x[0])+sum(x[1])==s, b)
        c = filter(lambda x: sorted([i for sub in x for i in sub])==sorted(data), c)
        # `res` = [min_diff_between_sum_of_numbers_in_two_sets,
        #           ((set_1), (set_2))
        #         ]
        res = sorted([(abs(sum(i[0]) - sum(i[1])), i) for i in c],
                key=lambda x: x[0])
        return min([i[0] for i in res])
    
    if __name__ == '__main__':
        assert min_diff_sets([10, 10]) == 0, "1st example"
        assert min_diff_sets([10]) == 10, "2nd example"
        assert min_diff_sets([5, 8, 13, 27, 14]) == 3, "3rd example"
        assert min_diff_sets([5, 5, 6, 5]) == 1, "4th example"
        assert min_diff_sets([12, 30, 30, 32, 42, 49]) == 9, "5th example"
        assert min_diff_sets([1, 1, 1, 3]) == 0, "6th example"
    
    0 讨论(0)
  • 2020-11-27 11:44

    Here is recursive approach

    def helper(arr,sumCal,sumTot,n):
        if n==0:
            return abs(abs(sumCal-sumTot)-sumCal)
        
        return min(helper(arr,sumCal+arr[n-1],sumTot,n-1),helper(arr,sumCal,sumTot,n-1))
    
    def minimum_subset_diff(arr,n):
        sum=0
        for i in range(n):
            sum+=arr[i]
            
        return helper(arr,0,sum,n)
    
    

    Here is a Top down Dynamic approach to reduce the time complexity

    dp=[[-1]*100 for i in range(100)]
    def helper_dp(arr,sumCal,sumTot,n):
        if n==0:
            return abs(abs(sumCal-sumTot)-sumCal)
        
        if dp[n][sumTot]!=-1:
            return dp[n][sumTot]
        
        return min(helper_dp(arr,sumCal+arr[n-1],sumTot,n-1),helper_dp(arr,sumCal,sumTot,n-1))
    
    def minimum_subset_diff_dp(arr,n):
        sum=0
        for i in range(n):
            sum+=arr[i]
            
        return helper_dp(arr,0,sum,n)
    
    
    0 讨论(0)
  • 2020-11-27 11:45

    Please check this logic which I have written for this problem. It worked for few scenarios I checked. Please comment on the solution, Approach :

    1. Sort the main array and divide it into 2 teams.
    2. Then start making the team equal by shift and swapping elements from one array to other, based on the conditions mentioned in the code.

    If the difference is difference of sum is less than the minimum number of the larger array(array with bigger sum), then shift the elements from the bigger array to smaller array.Shifting happens with the condition, that element from the bigger array with value less than or equal to the difference.When all the elements from the bigger array is greater than the difference, the shifting stops and swapping happens. I m just swapping the last elements of the array (It can be made more efficient by finding which two elements to swap), but still this worked. Let me know if this logic failed in any scenario.

    public class SmallestDifference {
    static int sum1 = 0, sum2 = 0, diff, minDiff;
    private static List<Integer> minArr1;
    private static List<Integer> minArr2;
    private static List<Integer> biggerArr;
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        SmallestDifference sm = new SmallestDifference();
        Integer[] array1 = { 2, 7, 1, 4, 5, 9, 10, 11 };
        List<Integer> array = new ArrayList<Integer>();
        for (Integer val : array1) {
            array.add(val);
        }
        Collections.sort(array);
        CopyOnWriteArrayList<Integer> arr1 = new CopyOnWriteArrayList<>(array.subList(0, array.size() / 2));
        CopyOnWriteArrayList<Integer> arr2 = new CopyOnWriteArrayList<>(array.subList(array.size() / 2, array.size()));
        diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
        minDiff = array.get(0);
        sm.updateSum(arr1, arr2);
        System.out.println(arr1 + " : " + arr2);
        System.out.println(sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
        int k = arr2.size();
        biggerArr = arr2;
        while (diff != 0 && k >= 0) {
            while (diff != 0 && sm.findMin(biggerArr) < diff) {
                sm.swich(arr1, arr2);
                int sum1 = sm.getSum(arr1), sum2 = sm.getSum(arr2);
                diff = Math.abs(sum1 - sum2);
                if (sum1 > sum2) {
                    biggerArr = arr1;
                } else {
                    biggerArr = arr2;
                }
                if (minDiff > diff || sm.findMin(biggerArr) > diff) {
                    minDiff = diff;
                    minArr1 = new CopyOnWriteArrayList<>(arr1);
                    minArr2 = new CopyOnWriteArrayList<>(arr2);
                }
                sm.updateSum(arr1, arr2);
                System.out.println("Shifting : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
            }
            while (k >= 0 && minDiff > array.get(0) && minDiff != 0) {
                sm.swap(arr1, arr2);
                diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
                if (minDiff > diff) {
                    minDiff = diff;
                    minArr1 = new CopyOnWriteArrayList<>(arr1);
                    minArr2 = new CopyOnWriteArrayList<>(arr2);
                }
                sm.updateSum(arr1, arr2);
                System.out.println("Swapping : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
                k--;
            }
            k--;
        }
        System.out.println(minArr1 + " : " + minArr2 + " = " + minDiff);
    }
    
    private void updateSum(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
        SmallestDifference sm1 = new SmallestDifference();
        sum1 = sm1.getSum(arr1);
        sum2 = sm1.getSum(arr2);
    }
    
    private int findMin(List<Integer> biggerArr2) {
        Integer min = biggerArr2.get(0);
        for (Integer integer : biggerArr2) {
            if(min > integer) {
                min = integer;
            }
        }
        return min;
    }
    
    private int getSum(CopyOnWriteArrayList<Integer> arr) {
        int sum = 0;
        for (Integer val : arr) {
            sum += val;
        }
        return sum;
    }
    
    private void swap(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
        int l1 = arr1.size(), l2 = arr2.size(), temp2 = arr2.get(l2 - 1), temp1 = arr1.get(l1 - 1);
        arr1.remove(l1 - 1);
        arr1.add(temp2);
        arr2.remove(l2 - 1);
        arr2.add(temp1);
        System.out.println(arr1 + " : " + arr2);
    }
    
    private void swich(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
        Integer e;
        if (sum1 > sum2) {
            e = this.findElementJustLessThanMinDiff(arr1);
            arr1.remove(e);
            arr2.add(e);
        } else {
            e = this.findElementJustLessThanMinDiff(arr2);
            arr2.remove(e);
            arr1.add(e);
        }
        System.out.println(arr1 + " : " + arr2);
    }
    
    private Integer findElementJustLessThanMinDiff(CopyOnWriteArrayList<Integer> arr1) {
        Integer e = arr1.get(0);
        int tempDiff = diff - e;
        for (Integer integer : arr1) {
            if (diff > integer && (diff - integer) < tempDiff) {
                e = integer;
                tempDiff = diff - e;
            }
        }
        return e;
    }
    

    }

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