Descubra os 3 dígitos

Sem%20t%C3%ADtulo1

Pessoal, tem essa brincadeira de descobrir os 3 dígitos do cadeado com as pistas.

Teve um programador que desenvolveu um código para isso. Para quem tá começando como eu, esse código é bem complexo. Fico imaginando desenvolver isso. Alguém se arrisca a explicar?

import java.util.stream.IntStream;
public class Test {

public static void main(String[] args) {
    for (int i = 1; i <= 999; i++) {
        String s = String.format("%03d", i);
        int[] arr = {s.charAt(0) - '0', s.charAt(1) - '0', s.charAt(2) - '0'};
        if (test01(arr) && test02(arr) && test03(arr) && test04(arr) && test05(arr)) {
            System.out.println("resultado: " + s);
        }
    }
 private static boolean test01(int[] arr) {
    return (arr[0] == 2 && arr[1] != 8 && arr[2] != 9)
            || (arr[1] == 8 && arr[0] != 2 && arr[2] != 9)
            || (arr[2] == 9 && arr[0] != 2 && arr[1] != 8);
}

private static boolean test02(int[] arr) {
    return umNumeroCorretoNoLugarErrado(arr, new int[]{2, 1, 5});
}

private static boolean test03(int[] arr) {
    int[] idxOf = {
        indexOf(9, arr),
        indexOf(4, arr),
        indexOf(2, arr)
    };
    int[][] grid = {{0, 1, 2}, {1, 0, 2}, {2, 0, 1}};
    int[] arr2 = {9, 4, 2};
    for (int[] idx : grid) {
        if (idxOf[idx[0]] >= 0 && idxOf[idx[1]] >= 0 && idxOf[idx[2]] < 0) {
            return arr[idx[0]] != arr2[idx[0]] && arr[idx[1]] != arr2[idx[1]];
        }
    }
    return false;
}

private static boolean test04(int[] arr) {
    return indexOf(7, arr) < 0
            && indexOf(3, arr) < 0
            && indexOf(8, arr) < 0;
}

private static boolean test05(int[] arr) {
    return umNumeroCorretoNoLugarErrado(arr, new int[]{7, 8, 4});
}

private static boolean umNumeroCorretoNoLugarErrado(int[] arr, int[] arr2) {
    int[] idxOf = {
        indexOf(arr2[0], arr),
        indexOf(arr2[1], arr),
        indexOf(arr2[2], arr)
    };
    if (IntStream.of(idxOf).allMatch(n -> n < 0)) {
        return false;
    }
    int[][] grid = {{0, 1, 2}, {1, 0, 2}, {2, 0, 1}};
    for (int[] idx : grid) {
        if (idxOf[idx[0]] >= 0) {
            if (idxOf[idx[1]] >= 0 || idxOf[idx[2]] >= 0) {
                return false;
            }
            return arr[idx[0]] != arr2[idx[0]];
        }
    }
    return false;
}

private static int indexOf(int n, int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == n) {
            return i;
        }
    }
    return -1;
}

}

@author Vinicius Silva

Então, tem algumas coisas complexas da linguagem no código, como expressões Lambda, e métodos genéricos (Generics). Então, como está começando, não é uma boa ideia começar por esse código. No entanto, caso insista em ‘desvendar’ o que o tal programador fez, sugiro que vá executando o código por partes, e consultando o manual da linguagem para aprender sobre algum método nativo que não tenha conhecimento sobre o funcionamento. Para te estimular, comecei para você:

public static void main(String[] args) {
	for (int i = 1; i <= 999; i++) {
		String s = String.format("%03d", i);
		int[] arr = {s.charAt(0) - '0', s.charAt(1) - '0', s.charAt(2) - '0'};
		if (test01(arr) && test02(arr) && test03(arr) && test04(arr) && test05(arr)) {
			System.out.println("resultado: " + s);
		}
	}
}

1 - Método main
1.1 - Executa um laço FOR de 1 a 999, já que o os números têm três casas;
1.2 - Formata cada um dos valores inteiros de i (de 1 a 999) um String de três casas:
# Usa o método charAt: String.charAt(int index);
# Usa o método String.format(String format, Object... args);
-> String s = String.format("%03d", i);

Exemplo: 1 é formatado como "001"

1.3 - Declara e inicializa o array arr de três posições.
Quando i = 1, a posição 0 da String "001" é '0'. Então faz-se a operação (48 - 48) em ASCII ou (U0030 - U0030) em Unicode; a posição 1 da String "001" é '0'. Então a operação e o resultado é mesmo que para a posição 0; por fim, a posição 2 da String"001" é '1'. Então faz-se a operação (49 - 48)em ASCII ou (U0031 -U0030) em Unicode. Logo, na primeira iteração, o array arr será preenchido com:

arr[0] = 0, arr[1] = 0, arr[2] = 1

A operação de subtração é realizada para que se senha o número inteiro e não o seu correspondente na tabela ASCII ou Unicode. Caso a operação não fosse realizada, o array arr, dando como exemplo a primeira iteração, teria os seguintes valores:

arr[0] = 48; arr[1] = 48; arr[2] = 49 na tabela ASCII ou

arr[0] = U0030; arr[1] = U0030; arr[2] = U0031 na tabela Unicode;

1.4 - Verifica se o retorno dos métodos test01, test02, …, test05 é true. Caso seja, imprime o valor da String s.

private static boolean test01(int[] arr) {
	return (arr[0] == 2 && arr[1] != 8 && arr[2] != 9)
			|| (arr[1] == 8 && arr[0] != 2 && arr[2] != 9)
			|| (arr[2] == 9 && arr[0] != 2 && arr[1] != 8);
}

2 - Métodotest01
2.1 - O método recebe o array arr;
2.2.1 - Verifica se o valor de arr é, respectivamente, 2, um valor que não seja 8 e um valor que seja 9;

  -> arr[0] = 2, arr[1] != 8, arr[2] != 9

2.2.2 - Ou, verifica se o valor de arr é, respectivamente, 8, um valor que não seja 2 e um valor que
seja 9;

  -> arr[0] = 8, arr[1] != 2, arr[2] != 9

2.2.2 - Ou, verifica se o valor de err é, respectivamente, 9, um valor que não seja 2 e um valor que seja 8;

  -> arr[0] = 9, arr[1] != 2, arr[2] != 8

Se qualquer uma dessas verificações for verdadeira, o método test01 retorna true.

private static boolean test02(int[] arr) {
	return umNumeroCorretoNoLugarErrado(arr, new int[]{2, 1, 5});
}

3 - O método test02 chama o método umNumeroCorretoNoLugarErrado passando o array arr e um novo array que registra os valores inteiros 2, 1 e 5.

4 - […]

1 curtida

Muito obrigado pela aula. Mas o mais difícil é criar o algoritmo disso, saber como as variáveis vão se comportar. Ou seja, como saber a lógica desse desafio do cadeado só com base nas pistas.

Então @Leilson_Frantchelino, antes de codificar em uma determinada linguagem, grealmente se codifica em termos de algoritmo, mesmo que mentalmente. Com efeito, a primeira coisa a se tentar entender é se já viu ou desenvolveu algo semelhante, buscar algum conhecimento matemático que ajude a resolver a questão, ou seja buscar por padrões que podem ser utilizados para criar rotinas, métodos. Outro ponto muito importante é resolver ou saber como se resolve o problema. No entanto, talvez diferente do que possa parecer, não é trivial entender a totalidade sobre o que um programador pensou quando escreveu um determinado método, ou declaração. Tanto é que geralmente se recorre a comentários, documentação ou mesmo se inquire o programador, quando isso é possível, sobre o que determinado trecho de código faz, porque o programador pode ter pulado alguma etapa que ele julgou desnecessária, mas que quebra a linha de raciocínio de um programador estranho ao código ou ao projeto. Então, façamos o básico, isto é entender como se pode resolver o problema, tendo em mente que podem existir numerosas outras formas de formular um algoritmo e, consequentemente, codificar uma solução em uma linguagem de programação.

Card 1: Em 289 há um número correto, no lugar certo.
Logo, as combinações possíveis são:

1.1 – o numeral 2 está no lugar certo, mas 8 e 9 não:

2XY gera 298.

1.2 – o numeral 8 está no lugar certo, mas 2 e 9 não:

X8Y gera 982.

1.3 – o numeral 9 está no lugar certo, mas 2 e 8 não:

XY9 gera 829.

Card 2: Em 215 há um número correto, mas no lugar errado.
Logo, as combinações possíveis são:

2.1 – o numeral 2 é o número correto no lugar errado. Logo:

2.1.1 – o numeral 2 está no meio:

X2Y gera 125 ou 521.

2.1.2 – o numeral 2 está no fim:

XY2 gera 152 ou 512.

2.2 – o numeral 1 é o número correto no lugar errado. Logo:

2.2.1 – o numeral 1 está no começo:

1XY gera 125 ou 152.

2.2.2 – o numeral 1 está no fim:

XY1 gera 251 ou 521.

2.3 – o numeral 5 é número correto no lugar errado. Logo:

2.3.1 – o numeral 5 está no começo:

5XY gera 512 ou 521.

2.3.2 – o numeral 5 está no meio:

X5Y gera 251 ou 152.

Card 3: Em 942 há dois números corretos, mas nos lugares errados. Logo:

3.1 – os numerais 9 e 4 são os corretos nos lugares errados. Logo, o numeral 9 não pode estar no início e o numeral 4 não pode estar no meio:

294, 429 ou 492.

3.2 – os numerais 9 e 2 são corretos nos lugares errados. Logo, o numeral 9 não pode estar no início e o numeral 2 não pode estar no final:

249, 294 ou 429.

3.3 – os numerais 4 e 2 são corretos nos lugares errados. Logo, o numeral 4 não pode estar no meio e o numeral 2 não pode estar no final:

249, 294 ou 429.

Card 4: Em 738, nada está correto. Logo, é de se supor nem mesmo os números pertencem ao número procurado. Com efeito, pode-se descarta as possibilidades descritas em 1.2 (X8Y).

Card 5: Em 784, um número correto mas no lugar errado. Pelo Card 4, sabemos que os numerais 7 e 8 não participam do número procurado. Logo, só resta o numeral 4 ser o correto no lugar errado. Então pode assumir a posição do meio ou a posição do início:

5.1 – no início:

4XY

5.2 – no meio:

X4Y

Com efeito, do Card 1 só sobra a possibilidade 1.3 (XY9) para as possibilidades de 5.1 (4XY). Logo:

4Y9.

Verificando o Card 2, de fato, há correspondência para as possibilidades 2.1.1 (X2Y) na possibilidade 4Y9 resultando em 429. Resta saber se o Card 3 prevê a possibilidade do número ser 429. Sim, em todas as combinações, o número 429 aparece como uma possibilidade.

Resposta: Os três dígitos procurados são 4, 2 e 9, nessa ordem.

Agora podemos perceber alguns padrões, ou melhor dicas semelhantes: tanto no Card2 quanto no Card 5 a dica é a mesma, logo, pode-se criar uma mesma rotina para ambos os testes. Com efeito, e a título de exemplo, podemos ter um método do tipo:

numeroCorretoLugarErrado(param p1, param p2); onde:

p1 é a sucessão de números de 0 a 942 (maior número informado nas dicas) e p2 é numeral informado na dica.

Além disso, no Card 3, podemos perceber três restrições ou possibilidade de combinação que restringem os números 942, 492 e 249, ou seja, ao menos em uma das possibilidades esses números não estão no conjunto das possibilidades. Logo, o número procurado não é nenhum deles. Se colocarmos o número do Card 3 em um vetor, teremos que 9 ocupará a posição 0, 4 a posição 1 e 2 a posição 2. Logo, podemos criar um vetor de restrições para cada uma dessas possibilidades:

Para 942:

vetorRestricao3_1 = {0, 1, 2};

Para 492:

vetorRestricao3_2 = {1, 0, 2};

Para 249:

vetorRestricao3_3 = {2, 0, 1};

Para facilitar podemos criar uma matriz 3 x 3 com esses vetores:

matrizRestricao = {{0, 1, 2}, {1, 0, 2}, {2, 0, 1}};

E por aí vai. À medida que se vai pensando em termos de implementação, alguns insights vão surgindo…

Obrigado @Iohannes, ajudou muito. Estou tentando implementar, vamos ver se consigo. Se tiver dúvidas, posto aqui. Já entendi a lógica, primeiro gerar possíveis combinações, depois ir filtrando conforme as dicas.

No primeiro card só existe um número correto, então se o 9 (nove) está presente, o 2 (dois) está descartado

Vou tentar fazer o meu aqui. Vou gerar todas a combinações possíveis, para depois ir filtrando de acordo com as dicas. Vou tentar usar Stream com filter(), mas não tenho tanto conhecimento assim. Qualquer coisa posto aqui para me ajudarem.

Correta a sua avaliação.