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.
-
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 isjava.util.List
. -
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
andMap
(from thejava.util
package) as types instead ofArrayList
andHashMap
. Here's what the result would be:private List<String> aParsear = new ArrayList<>(); private Map<Integer, String> map = new HashMap<>();
-
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);
-
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 theif
, but it is actually the outsidewhile
. 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. -
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) {
-
Your
aParsear
list actually consists of an array that you change to aString
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 thebusca
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);
-
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;
-
We can simplify your
if
with the twofor
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. } }
-
Note that your
Map
is only used within theparsearListaGerada
method and that after using the method, theMap
does not contain any information that is relevant to be preserved. Therefore, put theMap
as a local variable of this method. -
We can exchange
quantidade
forresultado.length
, and thus eliminate that variable. -
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 transformingint resultado[]
intoint[] resultado
. -
Assuming the list of numbers is always 1, 2, 3, 4… then you can eliminate the array
numeros
and replace thenumeros[x]
withx + 1
. -
A better name for your
aParsear
variable would bevolantes
. -
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);
inmain
, passing these magic numbers, you can introduce a constructor and a newbusca
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.