How to find if any values in array add up to n

|▌冷眼眸甩不掉的悲伤 提交于 2020-01-06 04:10:06

问题


I have an array of random values, and a target value.

#!/bin/bash
objective='50'
declare -a values=(1 2 2 6 8 14.5 15 28.7 .. 42)

I need to find a way to extract any combination of numbers in the array 'values' that add up to 50

The array has duplicates, and floating point integers.

A solution set might look like:

50 = 42 + 8
50 = 42 + 6 + 2

Initially I started in bash with some nested for loops, however I'm quickly realizing that this will grow exponentially with my array length.

I took a couple of java classes in college, but I'm still inexperienced in programming. I'm starting to think this may require recursion.

Can anyone with more programming experience point me in the right direction?
Besides nested for loops, how else could you approach this problem?


回答1:


Here is an algorithm that has time complexity O(M*N) whereas M is Target and N is total size of set. Use analogy with knapsack problem as follows :-

  1. Knapsack capacity = Target
  2. Items are elements in the set with weight & value same as itself
  3. Calculate maximum profit using dynamic programming
  4. maxprofit = Target then there is/are subset which sum up to target.
  5. Retrace the solution.

Java Solution for the same :-

public class SubSetSum {
    static int[][] costs;

    public static void calSets(int target,int[] arr) {

        costs = new int[arr.length][target+1];
        for(int j=0;j<=target;j++) {
            if(arr[0]<=j) {

                costs[0][j] = arr[0]; 
            }
        }
        for(int i=1;i<arr.length;i++) {

            for(int j=0;j<=target;j++) {
                costs[i][j] = costs[i-1][j];
                if(arr[i]<=j) {
                    costs[i][j] = Math.max(costs[i][j],costs[i-1][j-arr[i]]+arr[i]);
                }
            }

        }

        System.out.println(costs[arr.length-1][target]);
       if(costs[arr.length-1][target]==target) {
           System.out.println("Sets :");
           printSets(arr,arr.length-1,target,"");
       } 

       else System.out.println("No such Set found");

    } 

    public static void printSets(int[] arr,int n,int w,String result) {


        if(w==0) {
            System.out.println(result);
            return;
        }

        if(n==0) {
           System.out.println(result+","+arr[0]);
            return; 
        }

        if(costs[n-1][w]==costs[n][w]) {
            printSets(arr,n-1,w,new String(result));
        }
        if(arr[n]<=w&&(costs[n-1][w-arr[n]]+arr[n])==costs[n][w]) {
            printSets(arr,n-1,w-arr[n],result+","+arr[n]);
        }
    }

    public static void main(String[] args) {
        int[] arr = {1,2,3,8,9,7};
        calSets(10,arr);
    }
}



回答2:


I would do it like this:

1.some init

const int N=array size;
int val[N];   // input data
bool flag[N];  // is val used ?
for (int i=0;i<N;i++) flag[i]=false;
sort val[] descending

2.create function bool find_sum(int s);

  • if it found solution returns true else false
  • set flag to true for all used values

    {
    for (int i=0;i<N;i++)                    // find first usable big number (could be faster with binary search or with tailing last used i index into recursion)
     if ((val[i]<=s)&&(!flag[i]))
      {
      flag[i]=true;                           // flag it as used 
      if (val[i]==s) return true;           // try to find reminder
      if (find_sum(s-val[i])) return true; // if found return true 
      flag[i]=false;                         // else unflag val[i] and continue winth next number 
      }
    return false;                            // if sum not found then return false
    }
    

3.after find_sum(s) your sum consists of all val[i] where flag[i]!=false

[edit1] functional source tested even for your case [6,5,5] and sum=10 it is OK

//---------------------------------------------------------------------------
int find_sum(int *val,int *use,int N,int sum,bool init=true)
    {
    int i;
    if (init)
        {
        for (i=0;i<N;i++) use[i]=0;         // nothibg used yet
        for (int e=1;e;)                    // bubble sort
         for (e=0,i=1;i<N;i++)
          if (val[i-1]<val[i])
           { e=val[i-1]; val[i-1]=val[i]; val[i]=e; e=1; }
        }
    for (i=0;i<N;i++)                       // find first usable big number (could be faster with binary search or with tailing last used i index into recursion)
     if ((val[i]<=sum)&&(!use[i]))
        {
        use[i]=1;                           // val[i] is used
        if (val[i]==sum) return 1;              // try to find reminder
        if (find_sum(val,use,N,sum-val[i],false)) return 1; // if found return true
        use[i]=0;                           // else val[i] is unused and continue winth next number
        }
    return 0;                               // if sum not found then return false
    }
//---------------------------------------------------------------------------
void main()
    {
    int in[]={6,5,5}; // input data
    const int N=sizeof(in)/sizeof(int);
    int ret,out[N];
    if (find_sum(in,out,N,10))
     for (int i=0;i<N;i++)
      if (out[i])
        {
        cout << in[i] << " ";
        }
    }
//---------------------------------------------------------------------------

PS. in your question in the input array are also floating point values

  • so you have to change int val[],sum to float/double
  • and add some accuracy for sum comparison to work with floats

    if (val[i]==sum) return 1;
    
  • change to

    if (fabs(val[i]-sum)<1e-10) return 1;
    
  • or use any other accuracy instead of 1e-10




回答3:


You can use recursion yes, you can break the array into sub-parts (I use List).

  1. Start from 0th index of the Parent list and a blank list
  2. Iterate over to subList your Parent from i+1 to the end and thereby increasing your working list from 0 to i
  3. Check for the sum (calculated) equals your objective

Code:

static Integer[] array = { 1, 2, 2, 6, 8, 14, 15, 28, 30, 32, 12, 48, 6, 42 };
static int objective = 50;

public static void main(String args[]) {
    add(new ArrayList<Integer>(Arrays.asList(array)),
            new ArrayList<Integer>());
}

public static void add(List<Integer> digits, List<Integer> workingList) {

    for (int i = 0; i < digits.size(); i++) {
        // New sublist to store values from 0 to i
        List<Integer> list = new ArrayList<Integer>(workingList);
        list.add(digits.get(i));

        // Here you call this recursively with your Parent list from i+1
        // index and working list from 0 to i
        add(digits.subList(i + 1, digits.size()), list);
    }

    int sum = 0;
    for (int element : workingList) {
        sum += element;
    }

    if (sum == objective) {
        System.out.println(objective + " = "
                + Arrays.toString(workingList.toArray()));
    }

}

Output:

50 = [1, 2, 2, 15, 30]
50 = [1, 2, 6, 8, 15, 12, 6]
50 = [1, 2, 6, 14, 15, 12]
50 = [1, 2, 14, 15, 12, 6]
50 = [1, 2, 15, 32]
50 = [1, 2, 6, 8, 15, 12, 6]
...


来源:https://stackoverflow.com/questions/21670732/how-to-find-if-any-values-in-array-add-up-to-n

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!