Descubra os 3 dígitos

7 respostas
Leilson_Frantchelino

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

7 Respostas

I

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 - […]

Leilson_Frantchelino

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.

I

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…

Leilson_Frantchelino

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.

David_Yamakawa

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

Leilson_Frantchelino

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.

I

Correta a sua avaliação.

Criado 10 de abril de 2020
Ultima resposta 15 de abr. de 2020
Respostas 7
Participantes 3