web-dev-qa-db-fra.com

Comment diviser de manière optimale un tableau en deux sous-tableaux de manière à ce que la somme des éléments dans les deux soit identique, sinon donner une erreur?

Comment diviser de manière optimale un tableau en deux sous-tableaux de manière à ce que la somme des éléments des deux sous-tableaux soit identique, sinon donner une erreur?

Exemple 1

Étant donné le tableau

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

Il peut être divisé en 

10, 20, 30, 5, 40

et 

50, 40, 15

Chaque sous-tableau représente 105.

Exemple 2

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

Le tableau ne peut pas être divisé en 2 tableaux d'une somme égale. 

23
Samarth2011

Il existe une solution, impliquant une programmation dynamique, qui s'exécute dans O(n*TotalSum), où n est le nombre d'éléments du tableau et TotalSum est leur somme totale.

La première partie consiste à calculer l'ensemble de tous les nombres pouvant être créés en ajoutant des éléments au tableau.

Pour un tableau de taille n, nous appellerons ceci T(n),

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

(La preuve de l'exactitude est par induction, comme dans la plupart des cas de fonctions récursives.)

En outre, rappelez-vous, pour chaque cellule de la matrice dynamique, les éléments ajoutés afin de la créer.

Une simple analyse de complexité montrera que cela est fait dans O(n*TotalSum).

Après avoir calculé T(n), recherchez dans l’ensemble un élément ayant exactement la taille de TotalSum / 2.

Si un tel élément existe, les éléments qui l'ont créé, ajoutés ensemble, correspondent à TotalSum / 2 et les éléments qui ne faisaient pas partie de sa création sont également égaux à TotalSum / 2 (TotalSum - TotalSum / 2 = TotalSum / 2).

C'est une solution pseudo-polynomiale. Autant que je sache, ce problème n'est pas connu pour être dansP.

15
Gal

Cela s'appelle problème de partition . Il existe des solutions optimales pour certains cas particuliers. Cependant, en général, c'est un problème NP-complet.

8
blaze

Dans sa variante commune, ce problème impose 2 contraintes et cela peut être fait de manière plus simple.

  1. Si la partition ne peut être faite que quelque part sur la longueur du tableau (nous ne considérons pas les éléments en désordre)
  2. Il n'y a pas de nombre négatif.

L'algorithme qui fonctionne alors pourrait être:

  1. Avoir 2 variables, leftSum et rightSum
  2. Commencez à incrémenter leftSum en partant de la gauche et rightSum en partant de la droite du tableau.
  3. Essayez de corriger tout déséquilibre.

Le code suivant fait ce qui précède:

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);
}

Le résultat:

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

Bien entendu, si les éléments peuvent être combinés dans le désordre, il se transforme en problème de partition avec toute sa complexité.

2
Anirudh Ramanathan

J'ai essayé une solution différente. autres que les solutions Wiki (problème de partition).

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];
        }
    }

}

J'ai testé. (Cela fonctionne bien avec un nombre positif supérieur à 0) s'il vous plaît laissez-moi savoir si l'un des visage problème. 

0
Mani

Ceci est une solution récursive au problème, une solution non récursive pourrait utiliser une méthode d'assistance pour obtenir la somme des index 0 d'un index courant dans une boucle for et une autre solution pour obtenir la somme de tous les éléments du même index actuel. la fin, qui fonctionne. Maintenant, si vous voulez obtenir les éléments dans un tableau et comparer la somme, commencez par trouver le point (index) qui marque le déversé où la somme des deux côtés est égale, puis obtenez une liste et ajoutez les valeurs avant cet index et une autre liste après cet index. 

Voici le mien (récursion), qui détermine uniquement s'il existe un emplacement pour fractionner le tableau, de sorte que la somme des nombres d'un côté soit égale à la somme des nombres de l'autre côté. Inquiétude vis-à-vis d'indexOutOfBounds, qui peut facilement se produire en récursion, une légère erreur peut s'avérer fatale et générer de nombreuses exceptions et erreurs.

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

Ce problème indique que si un tableau peut avoir deux sous-tableaux avec leur somme d'éléments identiques . Une valeur booléenne doit donc être renvoyée . J'ai trouvé un algorithme efficace: Algo: Procedure Step. 1: Prenez un tableau vide en tant que conteneur, triez le tableau initial et conservez-le dans le vide ..___ Étape 2: prenez maintenant deux tableaux allouables dynamiquement et sortez le plus élevé et le deuxième plus élevé du tableau auxiliaire Etape 3: Comparez la somme des éléments dans les sous-tableaux, la plus petite somme aura la chance d'extraire le plus haut élément restant dans le tableau, puis de la supprimer du conteneur . Étape 4: Boucle par l’étape 3 jusqu’à ce que le conteneur soit vide .. Étape 5: Comparez la somme de deux sous-tableaux, s’ils sont identiques, renvoyez true, sinon false.

// La complexité de ce problème réside dans le fait qu’il peut y avoir de nombreuses combinaisons possibles, mais cet algorithme a une manière unique.

0
Samarth2011

On m'a posé cette question lors d'un entretien et j'ai donné ci-dessous une solution simple, car je n'avais pas vu ce problème sur aucun site Web auparavant.

Disons que le tableau A = {45,10,10,10,10,5} Ensuite, la division sera à index = 1 (index basé sur 0) de sorte que nous ayons deux ensembles de somme égaux {45} et { 10,10,10,10,5}

int leftSum = A[0], rightSum = A[A.length - 1];
int currentLeftIndex = 0; currentRightIndex = A.length - 1

/*Déplacez les deux pointeurs d'index vers le milieu du tableau jusqu'à currentRightIndex! = CurrentLeftIndex. Augmentez leftIndex si la somme des éléments de gauche est toujours inférieure ou égale à la somme des éléments situés à droite de 'rightIndex'. À la fin, vérifiez si leftSum == rightSum. Si true, nous avons obtenu l'index comme currentLeftIndex + 1 (ou simplement currentRightIndex, car currentRightIndex sera égal à currentLeftIndex + 1 dans ce cas) . * /

while (currentLeftIndex < currentRightIndex)
{
if ( currentLeftIndex+1 != currentRightIndex && (leftSum + A[currentLeftIndex + 1)     <=currentRightSum )
{
 currentLeftIndex ++;
 leftSum = leftSum + A[currentLeftIndex];
}


if ( currentRightIndex - 1 != currentLeftIndex && (rightSum + A[currentRightIndex - 1] <= currentLeftSum)
{
 currentRightIndex --;
 rightSum = rightSum + A[currentRightIndex];
}

}

if (CurrentLeftIndex == currentRightIndex - 1 && leftSum == rightSum)
PRINT("got split point at index "+currentRightIndex);
0
Kuldeep Tiwari

Le problème @Gal Subset-Sum est NP-Complete et possède un algorithme de programmation dynamique pseudo-polynomial O (n * TotalSum). Mais ce problème n'est pas NP-Complete. Il s’agit d’un cas spécial qui peut être résolu en temps linéaire.

Nous cherchons ici un index permettant de scinder le tableau en deux parties avec la même somme . Vérifier le code suivant.

Analyse: O (n), car l’algorithme itère uniquement dans le tableau et n’utilise pas 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

Solution trouvée ici

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
Andrey Mishenkin