Split sets

Asked

Viewed 67 times

0

Hello people need help with the following algorithm it must have as input an array where the user informs and the program shows the possible combinations, for example: input = [1,2,3] output = [ [1,2,3], [1,2], [1,3], [2,3], [1], [2], [3], [] ] If anyone can help me I’ll thank you very much!

  • 5

    What was tried? What was your difficulty? What did you not understand about the problem of taking the set of all subsets of another set?

  • what I did was to do the part of reading the vector with n numbers that the user informs, my difficulty is in assembling the subsets for generic cases

1 answer

1

Intuitively it is possible to realize that the number of possible combinations is equal to 2 n - 1 where n is the number of elements of the vector, so if the vector has an element the number of combinations is 1, if it has 2, the number of combinations is 3 and so on.

If the number of combinations is equal to 2 n - 1 it is possible to represent it with the bits that make up a int. For example, if the vector contains two numbers, each combination can be represented as follows:

[1][2] corresponds to 11, as both numbers appear.

[1] corresponds to 10, because only the first number appears.

[2] corresponds to 01, because only the second number appears.

So, let’s go to the implementation of the algorithm. In the code, I used the condition buffer_int % 2 == 0 to check whether the bit in position i is equal to 1 or equal to 0. And I used the instruction buffer_int /= 2; to move to the next bit. Check yourself:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>

int main(){

   int quantidade = 0;
   int numeros[31];
   /*31, pois (2^31 - 1) é o número máximo de combinações permitidade pelo programa.
   Seria relativamente fácil aumentar o número de combinações máxima para 2^63 - 1, 
   mas como o algoritmo tem complexidade exponencial, valores dessa grandeza não 
   são úteis do ponto de vista prático.*/  

   char buffer_str[64];

   //recebe os dados do usuario e cria o array
   while(quantidade < 31){
      printf("Entre um numero ou 'exit' para sair.\n");
      scanf("%s", &buffer_str);

      if(strcmp(buffer_str, "exit") == 0){
         break;
      }
      numeros[quantidade] = atoi(buffer_str);
      quantidade++;
   }

   int i;

   int32_t combinacao = 1;
   int buffer_int;

   while(combinacao < (int32_t) pow(2, quantidade)){

      buffer_int = combinacao;

      for(i = 0; i < quantidade; i++){
         //se o bit na posicao i é igual a 1 imprima o número
         if(buffer_int % 2 == 1){
            printf("%d ", numeros[i]);
         }
         //divide buffer_int por 2 para verificar o próximo bit
         buffer_int /= 2;
      }
      puts("");
      combinacao++;
   }
}

EDIT: I liked the suggestions made in the comments and made the following changes to the code:

  • I added the empty set to the set of possible combinations;
  • pow(), in fact, it is not safe to calculate an entire power, it has therefore been replaced;
  • replaces the while by a for, for agreeing that it was the most appropriate;
  • I used the function sprintf() and the variables elemento_str and conjunto_str to decrease excessive I/O and improve performance;
  • And finally, I also fixed a bug that happened when the user entered the maximum number of elements (31). I won’t go into details, but the right thing to do would be to use the guy uint32_t and not the kind int32_t.

    Below the excerpt of the changed code:

     int i;
    
       uint32_t num_comb = 1;
       //num_comb == numero de combinações possíveis
       for(int i = 0; i < quantidade; i++){num_comb *= 2;}
       uint32_t combinacao;
       uint32_t buffer_int;
    
       char elemento_str[8];
       //"[] é o primeiro conjunto a ser impresso (conjunto vazio)."
       char conjunto_str[128] = "[]";
    
       for(combinacao = 0; combinacao < num_comb; combinacao++){
          buffer_int = combinacao;
    
          for(i = 0; i < quantidade; i++){
             //se o bit na posicao i é igual a 1 imprima o número
             if(buffer_int % 2 == 1){
                sprintf(elemento_str, "%d ", numeros[i]);
                strcat(conjunto_str, elemento_str);
             }
             //divide buffer_int por 2 para verificar o próximo bit
             buffer_int /= 2;
          }
         printf("%s\n", conjunto_str);
         conjunto_str[0] = '\0';
       }
    
    
    • Just one detail: the trivial empty subset is also in the expected result, and empty is always possible to be obtained. Then, the number of possible subsets to form with n elements is yes 2^n. Even when you have a set A, the mathematical operation used to indicate the set of all subsets is 2^A. I could see this when, by listing the subset options for 2 elements, you did not quote the choice 00, just the 11, 10, 01. For the rest, its mathematical basis seemed adequately solid, just finishing reading the code part.

    • @Jeffersonquesado, you’re right. I assumed that the empty set was not included and made the code in that sense. I’m also checking another part of the code, in the beginning, I tested it with something around 20 numbers, more than that, it was taking more than a minute to calculate. But after I put 31 digits, there was a problem and I’m checking it. Maybe something is out of range. Then, I edit everything.

    • We don’t agree with some details in the implementation (I would never write pow(2, quantidade) for an entire potentiation, for example; nor would it do anything of UI, just algorithm and transfer of results to be useful at another point of code; nor would that for disguised as while), but I see no problems in the code besides ignoring the empty subset (which can be easily included by doing combinacao = 0 in place of combinacao = 1).

    • with 30 numbers you have close to 1 billion subsets. In the case of 20 numbers, it is close to 1 million subsets. Take into account that for each set you have several write operations with printf, one for each number, plus line break. Ultimately, your program was slow because of I/O with the terminal. If it helps, try redirecting the output to a text file on the command line, it should greatly increase performance (but I don’t know if, even so, it is feasible to wait, I couldn’t estimate how many millions of I/O operations are done)

    • 1

      Coincidentally, I have just received a positive vote in this answer. At some point there I talk to avoid excessive I/O for competitive computing problems. It can be useful for you to improve the performance of your test. It has a function called sprintf which writes in a string what the printf writes to a terminal. All 100% memory, no I/O. Will make code harder to read, but would improve performance.

    • 1

      By the way, I found the formula to find out how many times you’ll print. Just exchange the 20 for the number that will be used; in the case of the 20, approximately 10.5 million writings will be called. See formula

    Show 1 more comment

    Browser other questions tagged

    You are not signed in. Login or sign up in order to post.