java – Organize groups

Question:

I couldn't find the logic to implement this.

So, I'll explain it another way for better understanding:

The algorithm combines 10 numbers into sets of 6, which gives a total of 210 wheels. Since, I need to put a condition to ensure that they don't show sets in which 5 of the 6 numbers are repeated.

Example: I have this combination 10 numbers in a set of 6.

1 2 3 4 5 6  //
1 2 3 4 5 7 
1 2 3 4 5 8 
1 2 3 4 5 9 
1 2 3 4 5 10 
1 2 3 4 6 7 
1 2 3 4 6 8
1 2 3 4 6 9
1 2 3 4 6 10
1 2 3 4 7 8  //
.......

Since, 5 of the 6 numbers in the set are always repeated. So I need the algorithm to show me only the sets that don't have the 5 repeated numbers. As I highlighted with // .

01  02  03  04  05  06 ; 
01  02  03  04  07  08 ;
01  02  03  04  09  10 ;
01  02  03  05  07  09 ;
01  02  03  05  08  10 ;
01  02  03  06  07  10 ;
01  02  03  06  08  09 ;
01  02  04  05  07  10 ;
01  02  04  05  08  09 ;
01  02  04  06  07  09 ;
01  02  04  06  08  10 ;
01  02  05  06  07  08 ;
01  02  05  06  09  10 ;
01  02  07  08  09  10 ;
03  04  05  06  07  08 ;
03  04  05  06  09  10 ;
03  04  07  08  09  10 ;
05  06  07  08  09  10 ;
package jogo;

import java.awt.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class Combinacoes {

    private int numeros[] = {1,2,3,4,5,6,7,8,9,10};  
    private int quantidade = 6;  
    private int resultado[] = new int[6]; 
    private ArrayList<String>aParsear = new ArrayList<String>();
    private HashMap<Integer,String> map = new HashMap<Integer,String>();
    private int count = 0;
    private String[] temp;

    private void busca(int inicio,int fim, int profundidade){  

        if ( (profundidade + 1) >= quantidade)  
            for(int x = inicio; x <= fim; x++){  
                resultado[profundidade] = numeros[x];  
                // faz alguma coisa com um dos resultados possiveis  
                count++;  

                String tmp = ""+  resultado[0] + ", "
                        + resultado[1] + ", "
                        + resultado[2] + ", "
                        + resultado[3] + ", "
                        + resultado[4] + ", "
                        + resultado[5] + ""; 
                System.out.println(tmp);
                aParsear.add(tmp);
            }  
        else  
            for(int x = inicio; x <= fim; x++){  
                resultado[profundidade] = numeros[x];  
                busca(x + 1,fim + 1,profundidade + 1);  
            }  
    }  

    private void parsearListaGerada(){
        Iterator<String> it = aParsear.iterator();

        while(it.hasNext()){
            String tmp = it.next();
            int[]valoresInteiros = new int[6];
            String[]valoresLiterais = tmp.split(",");
            for(int i = 0;i < valoresLiterais.length;i++){
                valoresInteiros[i]= Integer.parseInt(valoresLiterais[i].trim());
            }
            int chave=0;
            for(int j=0;j < valoresInteiros.length;j++){
                chave+=((j+1)*valoresInteiros[j]);
            }
            if(map.containsKey(chave))
                System.out.println("ger,"+tmp);
                map.put(chave,tmp);
            }

            System.out.println("Tamanho final:"+map.size());
            System.out.println("Resultado final");
            for(String valor:map.values())
                System.out.println(valor);            

        }



    public static void main(String args[]){  

        Combinacoes comb = new Combinacoes();  
        comb.busca(0, (10-6), 0);  
        System.out.println("Total de combinacoes: " + comb.count); 
        comb.parsearListaGerada();

    }  

}

Answer:

Simplifying the code

Let's take a look at your code.

  1. See this import:

     import java.awt.List;

    This is not the list you want to import. You're not using this List for anything, but what you actually wanted to import is java.util.List .

  2. These variables:

     private ArrayList<String>aParsear = new ArrayList<String>(); private HashMap<Integer,String> map = new HashMap<Integer,String>();

    You can use diamond syntax to avoid having to repeat generics. Also, it is good practice to code for an interface and not an implementation. That is, using List and Map (from the java.util package) as types instead of ArrayList and HashMap . Here's what the result would be:

     private List<String> aParsear = new ArrayList<>(); private Map<Integer, String> map = new HashMap<>();
  3. Let's see how you transform the array into a String :

     String tmp = ""+ resultado[0] + ", " + resultado[1] + ", " + resultado[2] + ", " + resultado[3] + ", " + resultado[4] + ", " + resultado[5] + "";

    This would be much simpler and much better if it did, and it doesn't depend on the size of the array being fixed at 6:

     StringJoiner sj = new StringJoiner(", "); IntStream.of(resultado).boxed().map(Object::toString).forEach(sj::add); String tmp = sj.toString();

    Or, if you don't have a problem with changing the format of the resulting String a bit:

     String tmp = Arrays.toString(resultado);
  4. Let's see your if :

     if (...) // Um monte de linhas aqui. else // Mais um monte de linhas aqui.

    Please use the keys in this case because it's very easy for you to mess up by not putting them. For example:

     if (algumaCoisa) System.out.println("Estou dentro do if."); System.out.println("Será que ainda estou dentro do if?");

    In fact, below, your code has this exact problem:

     if(map.containsKey(chave)) System.out.println("ger,"+tmp); map.put(chave,tmp); }

    I lost some time to understand the program because of that block up there. The indentation suggests that the } refers to the if , but it is actually the outside while . The fact that the code is incorrectly indented and does not use all the key pairs that it should end up inducing whoever is reading the code to error.

  5. See your while loop:

     Iterator<String> it = aParsear.iterator(); while(it.hasNext()){ String tmp = it.next();

    Don't use the Iterator directly if you don't have a good reason to. Prefer enhanced-for :

     for (String tmp : aParsear) {
  6. Your aParsear list actually consists of an array that you change to a String and then back to an array. This is a workaround. To solve this, you can change this code:

     aParsear.add(tmp);

    That's why:

     aParsear.add(resultado.clone());

    And with that, you can eliminate the tmp variable from the busca method if you want.

    This from here:

     private ArrayList<String>aParsear = new ArrayList<String>()

    Stays like this:

     private List<int[]> aParsear = new ArrayList<>();

    You can then simplify a lot of things in parsearListaGerada with this:

     for (int[] valoresInteiros : aParsear) { String tmp = Arrays.toString(valoresInteiros);
  7. You can replace this:

     System.out.println("Total de combinacoes: " + comb.count);

    That's why:

     System.out.println("Total de combinacoes: " + comb.aParsear.size());

    And then this here can be deleted:

     // faz alguma coisa com um dos resultados possiveis count++;

    And that too:

     private int count = 0; private String[] temp;
  8. We can simplify your if with the two for s:

     if ( (profundidade + 1) >= quantidade) for(int x = inicio; x <= fim; x++){ resultado[profundidade] = numeros[x]; // Algumas coisas. } else for(int x = inicio; x <= fim; x++){ resultado[profundidade] = numeros[x]; // Outras coisas. }

    And leave it like this:

     for (int x = inicio; x <= fim; x++) { resultado[profundidade] = numeros[x]; if (profundidade + 1 >= quantidade) { // Algumas coisas. } else { // Outras coisas. } }
  9. Note that your Map is only used within the parsearListaGerada method and that after using the method, the Map does not contain any information that is relevant to be preserved. Therefore, put the Map as a local variable of this method.

  10. We can exchange quantidade for resultado.length , and thus eliminate that variable.

  11. Prefer to use (String[] args) instead of (String args[]) . This way you preserve the declaration pattern [variable type before and variable name after] used almost everywhere else in the Java language. Otherwise there's something in the format [part of the variable's type first, variable name in the middle, rest of the variable's type after], which is a little confusing. The same goes for transforming int resultado[] into int[] resultado .

  12. Assuming the list of numbers is always 1, 2, 3, 4… then you can eliminate the array numeros and replace the numeros[x] with x + 1 .

  13. A better name for your aParsear variable would be volantes .

  14. To prevent your program from getting stuck on 10 elements in the game with 6 on the wheel and you having to call comb.buscar(0, (10-6), 0); in main , passing these magic numbers, you can introduce a constructor and a new busca method:

     private int[] resultado; private List<int[]> volantes = new ArrayList<>(); public Combinacoes(int quantidade) { this.resultado = new int[quantidade]; } public void busca(int numeros) { busca(0, numeros - resultado.length, 0); }

    And then in your main you do this:

     Combinacoes comb = new Combinacoes(6); comb.busca(10);

There are still a few other changes possible, but I think it's a good size here. The resulting code behaves exactly the same as your original question code (except that it shows pairs of square brackets for every sextuple in the output). See how your code looks like:

simplified code

import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Combinacoes {

    private int[] resultado;
    private List<int[]> volantes = new ArrayList<>();

    public Combinacoes(int quantidade) {
        this.resultado = new int[quantidade];
    }

    public void busca(int numeros) {
        busca(0, numeros - resultado.length, 0);
    }

    private void busca(int inicio, int fim, int profundidade) {
        for (int x = inicio; x <= fim; x++) {
            resultado[profundidade] = x + 1;
            if (profundidade + 1 >= resultado.length) {
                System.out.println(Arrays.toString(resultado));
                volantes.add(resultado.clone());
            } else {
                busca(x + 1, fim + 1, profundidade + 1);
            }
        }
    }  

    private void parsearListaGerada() {
        Map<Integer, String> map = new HashMap<>();
        for (int[] valoresInteiros : volantes) {
            String tmp = Arrays.toString(valoresInteiros);
            int chave = 0;
            for (int j = 0; j < valoresInteiros.length; j++) {
                chave += ((j + 1) * valoresInteiros[j]);
            }
            if (map.containsKey(chave)) {
                System.out.println("ger," + tmp);
            }
            map.put(chave, tmp);
        }

        System.out.println("Tamanho final:" + map.size());
        System.out.println("Resultado final");
        for (String valor : map.values()) {
            System.out.println(valor);
        }
    }

    public static void main(String[] args) {  
        Combinacoes comb = new Combinacoes(6);  
        comb.busca(10);  
        System.out.println("Total de combinacoes: " + comb.aParsear.size()); 
        comb.parsearListaGerada();
    }  
}

improving the code

I don't understand what your idea is with the chave and the Map , but there are some elements where the keys collide. For example:

[1, 2, 3, 4, 8, 9] - chave = 124
[1, 2, 3, 5, 6, 10] - chave = 124

[1, 2, 3, 4, 5, 9] - chave = 109
[1, 2, 4, 5, 6, 7] - chave = 109

[1, 2, 3, 5, 7, 9] - chave = 123
[1, 3, 4, 5, 6, 9] - chave = 123

This code will ultimately show where these collisions of these keys occur, showing the 71 places that have them (this is the final size).

Considering that the result you want in the table that is in your question, I don't think that this method's parsearListaGerada procedure makes sense for you. If I understand your question correctly, what you want is to eliminate results that have 5 or more repeated numbers. This method however only shows which steering wheels have keys colliding. I ended up throwing this method away.

To redo everything, first let's introduce a Volante class:

    private static final class Volante {
        private final Set<Integer> numeros;

        public Volante(int[] valores) {
            this.numeros = IntStream.of(valores).boxed().collect(Collectors.toCollection(TreeSet::new));
        }

        public int dissimilaridade(Volante outro) {
            List<Integer> interseccao = new ArrayList<>(numeros);
            interseccao.removeAll(outro.numeros);
            return interseccao.size();
        }

        @Override
        public String toString() {
            return numeros.toString();
        }
    }

The class name is self-explanatory. Her constructor converts an int[] to a Set<Integer> . The big thing is the dissimilaridade(Volante) method dissimilaridade(Volante) which measures how many numbers differ between one wheel and another.

With that, we can do the following method:

    private static List<Volante> limitarSimilaridade(List<Volante> volantes, int dissimilaridadeMinima) {
        List<Volante> dissimilares = new ArrayList<>(volantes.size());

        externo:
        for (Volante volante : volantes) {
            for (Volante dejaVu : dissimilares) {
                if (volante.dissimilaridade(dejaVu) < dissimilaridadeMinima) continue externo;
            }
            dissimilares.add(volante);
        }
        return dissimilares;
    }

This method gets a list of Volante if it returns another filtered list removing those that are too similar to each other. By "too similar" it is meant that they have a dissimilarity below the specified value with some other wheel in the list.

This is the complete code of what I did:

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public final class Combinacoes {

    private static final class Volante {
        private final Set<Integer> numeros;

        public Volante(int[] valores) {
            this.numeros = IntStream.of(valores).boxed().collect(Collectors.toCollection(TreeSet::new));
        }

        public int dissimilaridade(Volante outro) {
            List<Integer> interseccao = new ArrayList<>(numeros);
            interseccao.removeAll(outro.numeros);
            return interseccao.size();
        }

        @Override
        public String toString() {
            return numeros.toString();
        }
    }

    public static List<Volante> busca(int quantidade, int numeros) {
        List<Volante> volantes = new ArrayList<>();
        busca(volantes, new int[quantidade], 0, numeros - quantidade, 0);
        return volantes;
    }

    private static void busca(List<Volante> volantes, int[] resultado, int inicio, int fim, int profundidade) {
        for (int x = inicio; x <= fim; x++) {
            resultado[profundidade] = x + 1;
            if (profundidade + 1 >= resultado.length) {
                volantes.add(new Volante(resultado));
            } else {
                busca(volantes, resultado, x + 1, fim + 1, profundidade + 1);
            }
        }
    }  

    private static List<Volante> limitarSimilaridade(List<Volante> volantes, int dissimilaridadeMinima) {
        List<Volante> dissimilares = new ArrayList<>(volantes.size());

        externo:
        for (Volante volante : volantes) {
            for (Volante dejaVu : dissimilares) {
                if (volante.dissimilaridade(dejaVu) < dissimilaridadeMinima) continue externo;
            }
            dissimilares.add(volante);
        }
        return dissimilares;
    }

    public static void main(String[] args) { 
        List<Volante> volantes = busca(6, 10);
        System.out.println(volantes.toString().replace("], ", "]\n"));
        System.out.println("Total de combinacoes: " + volantes.size());

        List<Volante> filtrados = limitarSimilaridade(volantes, 2);
        System.out.println("Tamanho final: " + filtrados.size());
        System.out.println("Resultado final");
        System.out.println(filtrados.toString().replace("], ", "]\n"));
    }  
}

As you can see, I made a few more changes to the code besides the ones I listed above. Basically the idea was:

  • Leaving fields useful only in the method busca live only within that method.

  • Produce lists as a result of methods instead of just storing them.

When running this new program, here is the end of the output (after showing the 210 handwheels generated):

Tamanho final:18
Resultado final
[[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 7, 8]
[1, 2, 3, 4, 9, 10]
[1, 2, 3, 5, 7, 9]
[1, 2, 3, 5, 8, 10]
[1, 2, 3, 6, 7, 10]
[1, 2, 3, 6, 8, 9]
[1, 2, 4, 5, 7, 10]
[1, 2, 4, 5, 8, 9]
[1, 2, 4, 6, 7, 9]
[1, 2, 4, 6, 8, 10]
[1, 2, 5, 6, 7, 8]
[1, 2, 5, 6, 9, 10]
[1, 2, 7, 8, 9, 10]
[3, 4, 5, 6, 7, 8]
[3, 4, 5, 6, 9, 10]
[3, 4, 7, 8, 9, 10]
[5, 6, 7, 8, 9, 10]]

This is exactly the same list that you put in your question and that you were hoping to get an answer.

See here working on ideone.

Scroll to Top