web-dev-qa-db-ja.com

配列を2つのサブ配列に最適に分割して、両方の要素の合計が同じになるようにするにはどうすればエラーが発生しますか?

配列を2つのサブ配列に最適に分割して、両方のサブ配列の要素の合計が同じになるようにするにはどうすればエラーが発生しますか?

例1

与えられた配列

10,  20 , 30 , 5 , 40 , 50 , 40 , 15

次のように分割できます

10, 20, 30, 5, 40

そして

50, 40, 15

各サブアレイの合計は105です。

例2

10,  20,  30,  5,  40,  50,  40,  10

配列を等しい合計の2つの配列に分割することはできません。

26
Samarth2011

動的プログラミングを含むソリューションが存在し、O(n*TotalSum)で実行されます。ここで、nは配列内の要素の数であり、TotalSumはそれらの合計です。

最初の部分は、配列に要素を追加することで作成できるすべての数値のセットを計算することです。

サイズnの配列の場合、これをT(n)と呼びます。

_T(n) = T(n-1) UNION { Array[n]+k | k is in T(n-1) }
_

(正確性の証明は、再帰関数のほとんどの場合のように、帰納法によるものです。)

また、動的マトリックスの各セルについて、それを作成するために追加された要素を覚えておいてください。

単純な複雑さの分析は、これがO(n*TotalSum)で行われることを示します。

T(n)を計算した後、_TotalSum / 2_のサイズとまったく同じ要素のセットを検索します。

そのようなアイテムが存在する場合、それを作成して加算した要素は_TotalSum / 2_に等しく、作成の一部ではなかった要素も_TotalSum / 2_(_TotalSum - TotalSum / 2 = TotalSum / 2_)に等しくなります。

これは擬似多項式解です。知る限り、この問題は[〜#〜] p [〜#〜]にあることは知られていない。

15
Gal

これは パーティションの問題 と呼ばれます。いくつかの特別な場合に最適なソリューションがあります。ただし、一般的には、NP完全な問題です。

8
blaze

一般的な変形では、この問題は2つの制約を課し、より簡単な方法で実行できます。

  1. パーティションが配列の長さに沿ったどこかでしか実行できない場合(要素の順序が乱れているとは見なしません)
  2. 負の数はありません。

次に機能するアルゴリズムは次のとおりです。

  1. LeftSumとrightSumの2つの変数があります
  2. 配列の左からleftSum、右からrightSumの増分を開始します。
  3. 不均衡を修正してください。

次のコードは上記を実行します。

public boolean canBalance(int[] nums) {
  int leftSum = 0, rightSum = 0, i, j;
  if(nums.length == 1)
      return false;
  for(i=0, j=nums.length-1; i<=j ;){
      if(leftSum <= rightSum){
         leftSum+=nums[i];
         i++;
      }else{
         rightSum+=nums[j];
         j--;
      }
  }
  return (rightSum == leftSum);
}

出力:

canBalance({1, 1, 1, 2, 1})       → true    OK      
canBalance({2, 1, 1, 2, 1})       → false   OK      
canBalance({10, 10})              → true    OK          
canBalance({1, 1, 1, 1, 4})       → true    OK      
canBalance({2, 1, 1, 1, 4})       → false   OK      
canBalance({2, 3, 4, 1, 2})       → false   OK      
canBalance({1, 2, 3, 1, 0, 2, 3}) → true    OK      
canBalance({1, 2, 3, 1, 0, 1, 3}) → false   OK      
canBalance({1})                   → false   OK      
canBalance({1, 1, 1, 2, 1})       → true    OK

もちろん、要素を順不同で組み合わせることができる場合、その複雑さをすべて備えたパーティションの問題になります。

4

この関数は、合計が偶数の場合、合計が等しい数のリストを分割してバランスをとります。

Python3 solution:

def can_partition(a):
    mylist1 = []
    mylist2 = []
    sum1 = 0
    sum2 = 0

    for items in a:
        # Take total and divide by 2.
        total = sum(a)
        if total % 2 == 0:
            half = total//2
        else:
            return("Exiting, sum has fractions, total %s half %s" % (total, total/2))       
        mylist1.append(items)
    print('Total is %s total, half is %s' %(total, total/2))

    for i in a:
        sum1 = sum(mylist1)
        sum2 = sum(mylist2)
        if sum2 < half:
            mypop = mylist1.pop(0)
            mylist2.append(mypop)

    # Function to swtich numbers between the lists if sums are uneven.           
    def switchNumbers(list1, list2,switch_diff):
        for val in list1:
            if val == switch_diff:
                val_index = list1.index(val)
                new_pop = list1.pop(val_index)
                list2.append(new_pop)

    #Count so while do not get out of hand 
    count = len(a)       
    while count != 0: 
        sum1 = sum(mylist1)
        sum2 = sum(mylist2)
        if sum1 > sum2:
            diff = sum1 -half
            switchNumbers(mylist1, mylist2, diff)
            count -= 1
        Elif sum2 > sum1:
            diff = sum2 - half
            switchNumbers(mylist2, mylist1, diff)
            count -= 1
        else:
            if sum1 == sum2:
                print('Half sum1 sum2 :',half, sum1,sum2)
                break
        count -= 1

    return (mylist1, mylist2)

b = [ 2, 3, 4, 2, 3, 1, 2, 5, 4, 4, 2, 2, 3, 3, 2 ]
can_partition(b)

Output:
Total is 42 total, half is 21.0
Half sum1 sum2 : 21 21 21

([4, 4, 2, 2, 3, 3, 2, 1], [2, 3, 4, 2, 3, 2, 5])
0
Eappan Benjamin

別のソリューションを試してみました。 Wikiソリューション(パーティションの問題)以外。

static void subSet(int array[]) {
    System.out.println("Input elements  :" + Arrays.toString(array));

    int sum = 0;
    for (int element : array) {
        sum = sum + element;
    }
    if (sum % 2 == 1) {
        System.out.println("Invalid Pair");
        return;
    }

    Arrays.sort(array);
    System.out.println("Sorted elements :" + Arrays.toString(array));

    int subSum = sum / 2;

    int[] subSet = new int[array.length];
    int tmpSum = 0;
    boolean isFastpath = true;
    int lastStopIndex = 0;
    for (int j = array.length - 1; j >= 0; j--) {
        tmpSum = tmpSum + array[j];
        if (tmpSum == subSum) { // if Match found
            if (isFastpath) { // if no skip required and straight forward
                                // method
                System.out.println("Found SubSets 0..." + (j - 1) + " and "
                        + j + "..." + (array.length - 1));
            } else {
                subSet[j] = array[j];
                array[j] = 0;
                System.out.println("Found..");
                System.out.println("Set 1" + Arrays.toString(subSet));
                System.out.println("Set 2" + Arrays.toString(array));
            }
            return;
        } else {
            // Either the tmpSum greater than subSum or less .
            // if less , just look for next item
            if (tmpSum < subSum && ((subSum - tmpSum) >= array[0])) {
                if (lastStopIndex > j && subSet[lastStopIndex] == 0) {
                    subSet[lastStopIndex] = array[lastStopIndex];
                    array[lastStopIndex] = 0;
                }
                lastStopIndex = j;
                continue;
            }
            isFastpath = false;
            if (subSet[lastStopIndex] == 0) {
                subSet[lastStopIndex] = array[lastStopIndex];
                array[lastStopIndex] = 0;
            }
            tmpSum = tmpSum - array[j];
        }
    }

}

私はテストしました。 (0より大きい正の数でうまく機能します)いずれかの問題に直面した場合、私に知らせてください。

0
Mani

これは問題の再帰的な解決策です。1つの非再帰的な解決策はヘルパーメソッドを使用してforループのインデックス0の現在のインデックスを取得し、別の1つは同じ現在のインデックスからすべての要素の合計を取得して動作します。要素を配列に入れて合計を比較したい場合は、最初に両側の合計が等しい場所でこぼれをマークするポイント(インデックス)を見つけてからリストを取得し、そのインデックスと別のリストの前に値を追加しますそのインデックスの後。

これが私の(再帰)です。これは、一方の側の数値の合計が他方の側の数値の合計と等しくなるように配列を分割する場所があるかどうかを判断するだけです。再帰で簡単に発生する可能性のあるindexOutOfBoundsを心配すると、わずかな間違いが致命的であり、多くの例外とエラーが発生する可能性があります。

public boolean canBalance(int[] nums) {
  return (nums.length <= 1) ? false : canBalanceRecur(nums, 0);   
}
public boolean canBalanceRecur(int[] nums, int index){ //recursive version
  if(index == nums.length - 1 && recurSumBeforeIndex(nums, 0, index) 
  != sumAfterIndex(nums, index)){ //if we get here and its still bad
  return false;
  }
  if(recurSumBeforeIndex(nums, 0, index + 1) == sumAfterIndex(nums, index + 1)){
  return true;
  }
  return canBalanceRecur(nums, index + 1); //move the index up
}
public int recurSumBeforeIndex(int[] nums, int start, int index){
   return (start == index - 1 && start < nums.length) 
   ? nums[start] 
   : nums[start] + recurSumBeforeIndex(nums, start + 1, index);
}

public int sumAfterIndex(int[] nums, int startIndex){
  return (startIndex == nums.length - 1) 
  ? nums[nums.length - 1] 
  : nums[startIndex] + sumAfterIndex(nums, startIndex + 1);
}
0
WIll

この問題は、配列が同じ要素の合計を持つ2つのサブ配列を持つことができる場合に言います。したがって、ブール値が返されます。効率的なアルゴリズムを見つけました:Algo:手順ステップ1:空の配列をコンテナーとして取得し、初期配列をソートして、空の配列を保持します。ステップ2:動的に割り当て可能な2つの配列を取得し、補助配列から最高位と2番目に高い値を取り出して、それぞれ2つのサブ配列に保持し、補助配列から削除します。ステップ3:サブアレイ内のエレメントの合計を比較します。小さい方の合計は、アレイ内の最も高い残りのエレメントをフェッチし、コンテナーから削除する機会があります。ステップ4:コンテナーが空になるまで、ステップ3をループします。ステップ5:2つのサブ配列の合計を比較し、それらが同じ場合はtrueを返し、そうでない場合はfalseを返します。

//この問題の複雑さは、多くの組み合わせが可能なことですが、このアルゴリズムには独自の方法が1つあります。

0
Samarth2011

@Gal Subset-Sum問題はNP完全であり、O(n * TotalSum)疑似多項式動的計画法アルゴリズムを備えています。しかし、この問題はNP完全ではありません。これは特殊なケースであり、実際、これは線形時間で解決できます。

ここでは、配列を同じ合計の2つの部分に分割できるインデックスを探しています。次のコードを確認してください。

分析:O(n)。アルゴリズムは配列全体を反復するだけで、TotalSumは使用しません。

public class EqualSumSplit {

    public static int solution( int[] A ) {

        int[] B = new int[A.length];
        int[] C = new int[A.length];

        int sum = 0;
        for (int i=0; i< A.length; i++) {
            sum += A[i];
            B[i] = sum;
            // System.out.print(B[i]+" ");
        }   
        // System.out.println();

        sum = 0;
        for (int i=A.length-1; i>=0; i--) {
            sum += A[i];
            C[i] = sum;
            // System.out.print(C[i]+" ");
        }
        // System.out.println();

        for (int i=0; i< A.length-1; i++) {
            if (B[i] == C[i+1]) {
                System.out.println(i+" "+B[i]);
                return i;
            }
        }

        return -1;

    }

     public static void main(String args[] ) {
         int[] A = {-7, 1, 2, 3, -4, 3, 0};
         int[] B = {10, 20 , 30 , 5 , 40 , 50 , 40 , 15};        
         solution(A);
         solution(B);
     }

}
0
Vinay

見つかった解決策 ここ

package sort;

import Java.util.ArrayList;
import Java.util.List;

public class ArraySumSplit {

public static void main (String[] args) throws Exception {

    int arr[] = {1 , 2 , 3 , 4 , 5 , 5, 1, 1, 3, 2, 1};
    split(arr);

}

static void split(int[] array) throws Exception {
    int sum = 0;
    for(int n : array) sum += n;
    if(sum % 2 == 1) throw new Exception(); //impossible to split evenly
    List<Integer> firstPart = new ArrayList<Integer>();
    List<Integer> secondPart = new ArrayList<Integer>();
    if(!dfs(0, sum / 2, array, firstPart, secondPart)) throw new Exception(); // impossible to split evenly;
    //firstPart and secondPart have the grouped elements, print or return them if necessary.
    System.out.print(firstPart.toString());
    int sum1 = 0;
    for (Integer val : firstPart) {
        sum1 += val;
    }
    System.out.println(" = " + sum1);

    System.out.print(secondPart.toString());
    int sum2 = 0;
    for (Integer val : secondPart) {
        sum2 += val;
    }
    System.out.println(" = " + sum2);
}

static boolean dfs(int i, int limit, int[] array, List<Integer> firstPart, List<Integer> secondPart) {
    if( limit == 0) {
        for(int j = i; j < array.length; j++) {
            secondPart.add(array[j]);
        }
        return true;
    }
    if(limit < 0 || i == array.length) {
        return false;
    }
    firstPart.add(array[i]);
    if(dfs(i + 1, limit - array[i], array, firstPart, secondPart)) return true;
    firstPart.remove(firstPart.size() - 1);

    secondPart.add(array[i]);
    if(dfs(i + 1, limit, array, firstPart, secondPart)) return true;
    secondPart.remove(secondPart.size() - 1);
    return false;
}
}
0