配列を2つのサブ配列に最適に分割して、両方のサブ配列の要素の合計が同じになるようにするにはどうすればエラーが発生しますか?
与えられた配列
10, 20 , 30 , 5 , 40 , 50 , 40 , 15
次のように分割できます
10, 20, 30, 5, 40
そして
50, 40, 15
各サブアレイの合計は105です。
10, 20, 30, 5, 40, 50, 40, 10
配列を等しい合計の2つの配列に分割することはできません。
動的プログラミングを含むソリューションが存在し、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 [〜#〜]にあることは知られていない。
これは パーティションの問題 と呼ばれます。いくつかの特別な場合に最適なソリューションがあります。ただし、一般的には、NP完全な問題です。
一般的な変形では、この問題は2つの制約を課し、より簡単な方法で実行できます。
次に機能するアルゴリズムは次のとおりです。
次のコードは上記を実行します。
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
もちろん、要素を順不同で組み合わせることができる場合、その複雑さをすべて備えたパーティションの問題になります。
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])
別のソリューションを試してみました。 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より大きい正の数でうまく機能します)いずれかの問題に直面した場合、私に知らせてください。
これは問題の再帰的な解決策です。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);
}
この問題は、配列が同じ要素の合計を持つ2つのサブ配列を持つことができる場合に言います。したがって、ブール値が返されます。効率的なアルゴリズムを見つけました:Algo:手順ステップ1:空の配列をコンテナーとして取得し、初期配列をソートして、空の配列を保持します。ステップ2:動的に割り当て可能な2つの配列を取得し、補助配列から最高位と2番目に高い値を取り出して、それぞれ2つのサブ配列に保持し、補助配列から削除します。ステップ3:サブアレイ内のエレメントの合計を比較します。小さい方の合計は、アレイ内の最も高い残りのエレメントをフェッチし、コンテナーから削除する機会があります。ステップ4:コンテナーが空になるまで、ステップ3をループします。ステップ5:2つのサブ配列の合計を比較し、それらが同じ場合はtrueを返し、そうでない場合はfalseを返します。
//この問題の複雑さは、多くの組み合わせが可能なことですが、このアルゴリズムには独自の方法が1つあります。
@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);
}
}
見つかった解決策 ここ
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;
}
}