Performance, para for

Pessoal eu to calculando digito validador, e em um unico for, eu posso calcular os 2 ou mais digitos de um vez.

Porem quando o 1° digito é invalido, não se faz necessario calcular o segundo, e assim por diante… mais em termos de performance, percorrer 2x ou mais um for e testar c é valido a primeira e c não for não entrar no segundo for, e assim por diante, é mais vantajoso que fazer 1 unico for, e nele fazer a conta dos 2 digitos, ou mais, simultaneamente?

Atualmente eu esotou percorredo o for, e fazendo os calculos simultaneamente, mais ão sei c isso vai dar problema de performance.

sei que muita gente vai pedir, trechos de códigos pra entender a questão, então vai abaixo, só que o trecho ta uma PORCARIA pra ler, estou ate refactorando pra melhorar a leitura…

A quem quiser ler o código, eu tenho uma interface, que funciona para prover os digitos de uma forma mais facil ao código

private interface DigitSequence { /** * Recupera o digito do indice pedido, caso o indice esteja OutOfRange retorna * zero, sem gerar exceções. * @param index indice do valor buscado. * @return número de indice index */ public int getNumberAt(int index); /** * Dimensão desta sequencia de digitos * @return Dimensão desta sequencia de digitos */ public int length(); }

Antes de ler o restante do código é bom saber como funciona os calculos de validadores de CPF, CNPJ, Inscrições estaduais e etcs…
Em sintese todos eles tem um número basico, o numero que realmente conta, e digitos verificadores… a conta é bem simples…
cada digito verificador, é calculado multiplicando um peso especifico por cada digito do numero basico, depois usa-se um divisor para encontrar o digito.

Para o CPF existem 9 numeros baiscos os outros 2 são digitos verificador “209.749.249-53” portanto 209749249 é o número do CPF e 53 é a verificação.

Os pesos para calcular os digitos são
10,9,8,7,6,5,4,3,2 para o 1° digito … ou seja… deve-se somar 10x2 + 9x0 + 8x9 + … 9x2
11,10,9,8,7,6,5,4,3,0 para o 2° digito … note que o 2° digito tem um número a mais de peso, pois ele é multiplicado depois que acha-se o 1° digito verificador, no caso do CPF esse peso é 0, portanto é indiferente o resultado do 1° digito verificador.

O divisor pra o CPF é 11, portanto deve-se pegar a soma acima para cada digito, divir por 11, pegar o resto desta operação e fazer a conta 11-(esse resto) … e esse sera o digito verificador, salvo quando essa conta é maior que 9 e neste caso o digito passa a ser 0.

o CNPJ segue a mesma logica, porem os pesos são
5,4,3,2,9,8,7,6,5,4,3,2 para o 1° digito
6,5,4,3,2,9,8,7,6,5,4,3,2 para o 2° digito (note que para o CNPJ o resultado do 1° digito pesa na conta do 2°)

exemplos de uso

[code]//exemplo de uso para CPF
int[] c1 = {10,9,8,7,6,5,4,3,2}; //pesos do 1° digito do CPF
int[] c2 = {11,10,9,8,7,6,5,4,3}; //pesos do 2° digito do CPF, o ultimo peso 0 não precisa ser escrito, basta ver a interface DigitSequence para ver que qualquer número que se pegue além dos enumerados retornará 0.
int[][] c = {c1,c2}; //array de coeficientes
System.out.println( //Cpf tem 9 números basicos e o divisor é 11
VerifyDigit.isValid(“209.749.249-53”, 9, 11, c));

//exemplo de uso para CNPJ
System.out.println( //Cnpj tem 12 números basicos e o divisor é 11
VerifyDigit.isValid(“11.111.111/0001-81”, 12, 11, “543298765432”,“6543298765432”));
//como CNPJ usa numeros menores que 10 como peso, da pra passar todos os pesos em uma string ^^
[/code]
A sequencia para validação esta abaixo, O método isValid() tem varias formas de entrada, e todas converte para o modo privado mostrado abaixo
onde as entradas de sequencia de digitos são convertidas para interface DigitSequence

/** * Verifica se a sequencia de digitos <tt>validate</tt>, que tem dimensão basica * <tt>baseLength</tt> (o restante é digitos verificadores), onde cada digito * verificador utiliza o <tt>divisor</tt> e os pesos enviado na array de * {@link DigitSequence}[] <tt>coeficients</tt> * @param validate sequencia de digitos a ser validada. * @param baseLength dimensão basica da sequencia de digitos (por exemplo o CPF tem * dimensão basica = 9 os outros 2 são digitos verificadores). * @param divisor o divisor do teste dos digitos, por exemplo o CPF usa divisor 11 * @param coeficients uma array de DigitSequence com os pesos para cada digito * por exemplo para o CNPJ a array é<tt> * <BR>coeficients = {@link DigitSequence}[2]; * <BR>coeficients[0] = new {@link ArrayInteger}({5,4,3,2,9,8,7,6,5,4,3,2}); * <BR>coeficients[1] = new {@link StringInteger}("6543298765432"); * </tt> * @return <tt>true</tt> caso a sequencia de digitos contenha os verificadores * corretos, e <tt>false</tt> caso contrario. */ private static boolean isValid(DigitSequence validate,int baseLength, int divisor, DigitSequence ... coeficients ) { boolean result = false; int[] sum = new int[coeficients.length]; /* faz a 1° parte da soma para todos os digitos simultaneamente, ate o final * dos digitos basicos. */ for (int i = 0 ; i < baseLength ; i++) { int number = validate.getNumberAt(i); for (int j = 0 ; j < coeficients.length ; j++) { sum[j] += number * coeficients[j].getNumberAt(i); } } int[] digit = new int[coeficients.length]; boolean needTeste = true; int cursor = 0; /* Calcula cada digito verificador e já testa c ele bate com o enviado, caso * não sejam iguais, já para o teste e retorna falso, caso ainda exista mais * teste, após calcular um digito, já realiza as somas para os digitos seguintes * que esperavam por esse resultado, para multiplicar pelos respectivos pesos. */ while (needTeste) { digit[cursor] = divisor - (sum[cursor]%divisor); if (digit[cursor] > 9) digit[cursor] = 0; result = validate.getNumberAt(baseLength + cursor) == digit[cursor]; cursor++; if (cursor + baseLength == validate.length() || !result) needTeste = false; else { for (int j = cursor ; j < coeficients.length ; j++) { sum[j] += digit[cursor-1] * //digito recem calculado coeficients[j].getNumberAt(baseLength + cursor -1); //peso } } } return result; }

Na maioria dos casos, prefira usar apenas um for por questões de performance (a menos que isso aumente muito a complexidade do código). Como você próprio já disse, o código está bem bagunçado e eu nem olhei direito. Mas você disse que quando um dígito for inválido deve parar a verificação, certo? Mas isso já não está sendo feito? Quando ‘result’ é false, ‘needTest’ recebe false parando a verificação. Não é isso que você quis dizer? Ou eu entendi errado?

+ou- … o problem é assim… quando vc calcula os digitos basicos, é tudo igual para todos os digitos verificadores… e esse código ta no trecho la de cima…

depois… só no trecho dos digitos verificaores… eu to testando c preciso fazer o for final…

+ou - assim… par ao CNPJ “11.111.111/0001-81” … eu faço a conta do 1° e do 2° digito juntos, ate antes de chegar no 81 …

quando vou para o 8 eu calculo qual é o digito, se o digito for realmente 8, eu vou para o seungod digito, adciono a soma 8x2 a segundo a soma, e faço a conta para o segundo digito para ver c realmente é um…

na essencia eu to fazendo as duas contas ao mesmo tempo, pois c vc olhar CNPJ tem 14 números, e 12 deles eu já calculei, so nos 2 ultimos que eu fiz o teste antes de calcular o resto

eu posso separar… não tem problemas de complexidade nenhuma, só não sei c vale a pena…

o fato é… preciso percorrer os 14 digitos de um CNPJ, para calcular o 1° digito
depois preciso percorrer os mesmo 14 digitos de um CNPJ, para calcular o 2° digito…

mais c o 1° não bate, não há necessidade de calcular o 2°

mas vale a pena eu separar essas contas ?? conciderando que da pra calcular as 2 ao mesmo tempo, quando percorro um único for…

pergunto isso por questões de performance.

Eu tenho certeza que quando o CNPJ é invalido, vale mais a pena percorrer só um for, mais no caso dos 2 digitos serem validos, eu não perdi performance ? percorrendo o FOR 2 x ?? podendo fazer isso num unico for ??

Quantos milhões de CPFs você pretende calcular por vez? Ou a validação é só na entrada de dados?

Se for na entrada… nenhum dos dois vai ter uma diferença significativa de performance. Para seu usuário, esperar 1 ou 2 centésimos de segundo não faz a menor diferença.

uma única vez, e apenas se for chamado isValid() como vc pode ver, eu so realizo o teste, caso ele não fora realizado ainda para esta instancia…

meu medo é isso dentro de um loop grande de 1000 por exemplo, é mais uma questão de saber qual dos 2 casos comeria mais processo

[code]import java.util.regex.Pattern;
import org.lavieri.util.vd.VerifierDigits;

public class Cpf {
/**
* Pesos para o calculor de dos digitos verificadores do CPF.
/
private static final int[][] COEFICIENTS =
{
{10, 9,8,7,6,5,4,3,2},//pesos para o 1° digito verificador
{11,10,9,8,7,6,5,4,3} //pesos para o 2° digito verificador
};
/
*
* Número de digitos de um CPF (excluindo-se os digitos verificadores)
/
private static final int BASE_LENGTH = 9;
/
*
* Fator de divisão, usado no calculo dos digitos verificadores.
*/
private static final int FACTOR = 11; //fator de divisão para calculo dos digitos

/** 
 * Verificação se este CPF é valido, null significa que o teste ainda não foi 
 * realizado, so é verificar uma vez, apois a primeira chamada de isValid() 
 * @see isValid() 
 */ 
@Transient private Boolean valid = null; 
/** 
 * String contendo o valor deste CPF 
 */ 
@Column(unique=true) private String cpf; 

//... 
// outros métodos 
//... 

public boolean isValid() { 
    if (valid == null) 
        valid = invalidPeculiarity();
    return valid; 
}
/**
 * Verifica primeiramente todas as particularidades do CPF (como se todos os digitos
 * são iguais a zero) para só depois caso necessario fazer fazer o teste dos digitos 
 * verificadores
 * @return <tt>true</tt> - quando cpf é valido
 * 			<br><tt>false</tt> - quando o cpf é invalido.
 */
private boolean isValidPeculiarity() {
	Pattern allIsZero = Pattern.compile("0{11}");
	valid = !allIsZero.matcher(this.cpf).find();
	if (valid)
		valid = VerifierDigits.isValid(this.cpf, BASE_LENGTH, FACTOR, COEFICIENTS);
	return valid
}

}[/code]

Você está com medo à toa. Use o que deixar o código mais legível.

Otimização assim, só se vc realmente tiver um problema na mão. E, acredite, esse problema só vai ser percebido na casa dos 100 ou 200 mil CPFs calculados, se não mais.

Não deveria ser “getDigittAt” ?

Em matemática isso chama-se combinação linear.
Na realidade vc está fazendo o produto interno entre dois vetores

Para o CPF 20974924953" vc faz o produto interno com “10,9,8,7,6,5,4,3,2,0,0” e depois com “11,10,9,8,7,6,5,4,3,0”

Na realidade vc pode executar o dois produtos em um unico for. dando mais eficiencia. Mesmo vc podendo quebrar na primeira verificação.

o codigo eficiente seria 1 “for” + 1 if + 1 if
Se vc dividiir o calculo em dois for ,teria 1 for + 1 if + 1 for + 1if , que é obviamente menos eficiente.

O negocio do modulo 11 é detalhes.