Perdido y desesperado con este código

Iniciado por pacosn1111, 21 Abril 2015, 22:34 PM

0 Miembros y 1 Visitante están viendo este tema.

pacosn1111

Hola de nuevo, como sabeis estoy escribiendo un motor de cifrado en C, pero no hay forma de hacerlo funcionar correctamente, os dejo el código:


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>

// variables globales

double claves[27][1000];
int clave[27];


// arrays globales
char letras[]="abcdefghijklmnopqrstuvwxyz ";
char cifrado[999999999];
char descifrado[999999999];

// prototipos de funciones
void generar_clave(int);
void ingresar_clave(int []);
int comprobar_repetir_vector(int, int []);
int comprobar_repetir_matriz(double, double [][1000]);
void arrancar_motor(int, int);
int suma(double);
double aleatorio(int, int);
void cifrar(int, int, char []);
void descifrar(int, char []);

int main() {

srand(getpid());
generar_clave(15);
arrancar_motor(15, 1);



}


// comprueba si un numero esta repetido en una matriz, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_matriz(double numero, double matriz[][1000]) {

int x;
int y;
for(x=0; x<27; x++) {

for(y=0;y<1000;y++) {

if (numero==matriz[x][y]) {
return 1;
}
}

}
return 0;

}

// comprueba si un numero esta repetido en un vector, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_vector(int numero, int vector[]){

int x;
for(x=0; x<27;x++) {

if(numero==vector[x]) {

return 1;

}
}
return 0;
}

// Devuelve la suma de las cifras de un numero
int suma(double numero) {

int resultado=0;
while (numero>0) {

resultado+=fmod(numero, 10);
numero/=10;
}
return resultado;
}

//Genera un numero aleatorio sin decimales dentro de un rango entre max y min.
double aleatorio(int max, int min) {


return rand() % max + min;


}
//Genera una clave optimidada y la guarda en la variable global clave.
void generar_clave(int numero_suma) {


int maximo, minimo, x, y, num_random;

int max=120;
int min=60;

maximo=numero_suma*max/20;
minimo=numero_suma*min/20;


for(x=0;x<27;) {
num_random=aleatorio(maximo, minimo);
if (comprobar_repetir_vector(num_random, &clave[0])==1) {
continue;
}
clave[x]=num_random;
x++;
}

printf("se ha generado la clave: \n");
for(y=0;y<27;y++) {

printf("(%d)", clave[y]);

}
printf("\n");
}

// Permite ingresar el valor de cualquier array en la variable global clave.
void ingresar_clave(int array[]) {

int x;

for(x=0;x<27;x++) {

clave[x]=array[x];
}

}
// Genera los numeros aleatorios correspondientes a cada letra y los guarda según corresponda en la matriz claves.
void arrancar_motor(int numero_cifras, int cantidad_numeros){

srand(time(NULL));
int y, z, h;
double num_random;

double min=(double)pow(10, numero_cifras-1);
double max=(double)pow(10, numero_cifras)-1;



for(z=0; z<27; z++) {
printf("Inicializando letra %c\n", letras[z]);
for(h=0; h<cantidad_numeros;) {
num_random=aleatorio(max, min);
if ((suma(num_random)==clave[z]) && (comprobar_repetir_matriz(num_random, claves))==0) {

claves[z][h]=num_random;
printf("%.0lf\n", num_random);
h++;



} else { continue;  }


}
}
printf("se ha generado la clave: \n");
for(y=0;y<27;y++) {

printf("(%d)", clave[y]);

}
printf("\ncon un numero de cifras de %d\n", numero_cifras);

printf("minimo: %lf:", min);
printf("maximo: %lf:", max);
}

// Cifra un texto usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global cifrado.
void cifrar(int numero_cifras, int cantidad_numeros, char texto[]) {


int letra, letrass, x;
char cifrado_inside[strlen(texto)*numero_cifras];
char string_auxiliar[numero_cifras-1];
for(letra=0; strlen(texto)>letra; letra++) {


for(letrass=0; letrass<27; letrass++) {

if (texto[letra]==letras[letrass]) {
sprintf(string_auxiliar, "%.0lf", claves[letrass][(int)(aleatorio(cantidad_numeros-1, 0))]);
strcat(cifrado_inside, string_auxiliar);
break;
}

}

}
strcpy(cifrado, cifrado_inside);

}

// Descifra un texto cifrado anteriormente usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global descifrado.
void descifrar(int numero_cifras, char cifrado[]) {


char auxiliar[numero_cifras];
int x, y=0, z;

for(x=0; x<strlen(cifrado); x++) {

auxiliar[y]=cifrado[x];
y++;


if(y==numero_cifras) {

for(z=0; z<27; z++) {

if(suma(atof(auxiliar))==clave[z]) {

descifrado[strlen(descifrado)]=letras[z];
y=0;
break;


}


}


}





}



}



La funcion aleatorio devuelve un numero aleatorio dentro de un rango, la función generar_clave genera una clave optimizada y la función arrancar_motor genera x numeros de x cifras cuyas cifras sumen x numero y lo guarda en un array para posteriormente cifrar o descifrar con esos numeros, pero se me ha atascado la funcion arrancar_motor, no funciona lo rapido que esperaba, ahora hasta se queda en "inicializando letra a" y de ahí no sale.

Gracias de antemano.

Saludos.

eferion

1. Variables no inicializadas

if (comprobar_repetir_vector(num_random, clave)==1)

int comprobar_repetir_vector(int numero, int *vector )
{
int x;
for(x=0; x<27;x++)
  {
if(numero==vector[x])
return 1;
}
return 0;
}


Si te das cuenta, no estás dando a "clave" unos valores iniciales... simplemente le asignas un valor y después verificas que dicho valor no esté repetido. ¿Problemas? Varios:


  • "clave" tendrá inicialmente valores aleatorios. Si uno de estos valores coincide con el que intentas ingresar el algoritmo dará este nuevo valor como no válido.
  • No tiene sentido que para cada valor que intentas añadir a "clave" compruebes sus 27 posiciones... lo suyo sería comprobar desde 0 hasta N-1, siendo N el índice del número actual. De esta forma únicamente comparas el número actual con los ya insertados en el arreglo. Corrigiendo este punto podrías olvidarte del problema del punto anterior
  • No es buena idea tener el "27" a pelo en el código. ¿Qué sucede si luego tienes que crear una clave de 40 elementos? Te toca editar ese número en varias partes del código... es bastante recomendable tener estos valores en un #define para evitar errores

2. Cuidado con los decimales

Cuando trabajes con números decimales (float, double, ... ) tienes que tener muy en cuenta que NUNCA debes hacer una comparación directa, tipo "if( float == float )". ¿Por qué? Bueno, pues porque un número en coma flotante nunca va a ser 100% preciso. Los números en coma flotante tienen una serie de dígitos significativos, mientras que el resto pueden ser considerados "basura". Si tu fuerzas a una comparación exacta, esa basura va a hacer que la comparación sea bastante inestable. La forma de comparar si un número decimal es igual a otro pasa por asumir que dos números son iguales si el resto entre ellos es menor a X. Ejemplo:

Código (cpp) [Seleccionar]

int main( )
{
  // Un float nunca es preciso 100%.
  // Esta inicialización intenta reflejar esta situación
  float a = 5.0001, b;
  for( b=1.0; b < 10.0; b+=1.0 )
  {
    if( a == b )
    {
      printf( "Bucle 1 OK!!!\n" );
      break;
    }
  }

  for( b=1.0; b < 10.0; b+=1.0 )
  {
    if( fabs( a - b ) < 0.001 )
    {
      printf( "Bucle 2 OK!!!\n" );
      break;
    }
  }
}


¿Dónde realizas este tipo de comprobaciones? Por ejemplo en la función "suma", que se llama desde "arrancar_motor"... esa es, al menos, una de las razones por las que el código "tarda mucho".

Un saludo

pacosn1111

Cita de: eferion en 22 Abril 2015, 08:46 AM
1. Variables no inicializadas

if (comprobar_repetir_vector(num_random, clave)==1)

int comprobar_repetir_vector(int numero, int *vector )
{
int x;
for(x=0; x<27;x++)
  {
if(numero==vector[x])
return 1;
}
return 0;
}


Si te das cuenta, no estás dando a "clave" unos valores iniciales... simplemente le asignas un valor y después verificas que dicho valor no esté repetido. ¿Problemas? Varios:


  • "clave" tendrá inicialmente valores aleatorios. Si uno de estos valores coincide con el que intentas ingresar el algoritmo dará este nuevo valor como no válido.
  • No tiene sentido que para cada valor que intentas añadir a "clave" compruebes sus 27 posiciones... lo suyo sería comprobar desde 0 hasta N-1, siendo N el índice del número actual. De esta forma únicamente comparas el número actual con los ya insertados en el arreglo. Corrigiendo este punto podrías olvidarte del problema del punto anterior
  • No es buena idea tener el "27" a pelo en el código. ¿Qué sucede si luego tienes que crear una clave de 40 elementos? Te toca editar ese número en varias partes del código... es bastante recomendable tener estos valores en un #define para evitar errores

2. Cuidado con los decimales

Cuando trabajes con números decimales (float, double, ... ) tienes que tener muy en cuenta que NUNCA debes hacer una comparación directa, tipo "if( float == float )". ¿Por qué? Bueno, pues porque un número en coma flotante nunca va a ser 100% preciso. Los números en coma flotante tienen una serie de dígitos significativos, mientras que el resto pueden ser considerados "basura". Si tu fuerzas a una comparación exacta, esa basura va a hacer que la comparación sea bastante inestable. La forma de comparar si un número decimal es igual a otro pasa por asumir que dos números son iguales si el resto entre ellos es menor a X. Ejemplo:

Código (cpp) [Seleccionar]

int main( )
{
  // Un float nunca es preciso 100%.
  // Esta inicialización intenta reflejar esta situación
  float a = 5.0001, b;
  for( b=1.0; b < 10.0; b+=1.0 )
  {
    if( a == b )
    {
      printf( "Bucle 1 OK!!!\n" );
      break;
    }
  }

  for( b=1.0; b < 10.0; b+=1.0 )
  {
    if( fabs( a - b ) < 0.001 )
    {
      printf( "Bucle 2 OK!!!\n" );
      break;
    }
  }
}


¿Dónde realizas este tipo de comprobaciones? Por ejemplo en la función "suma", que se llama desde "arrancar_motor"... esa es, a
l menos, una de las razones por las que el có
digo "tarda mucho".

Un saludo

Gracias, muy buena explicación, en cuanto llegue a casa (estoy en el trabajo) lo implemento y os cuento que tal va.

Saludos.

pacosn1111

#3
Hola otra vez, he corregido los fallos que me has comentado y los he corregido, os dejo el código corregido:


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>

// constantes

#define numero_de_letras 27

// variables globales

double claves[numero_de_letras][1000];
int clave[numero_de_letras];


// arrays globales
char letras[]="abcdefghijklmnopqrstuvwxyz ";
char cifrado[999999999];
char descifrado[999999999];

// prototipos de funciones
void generar_clave(int);
void ingresar_clave(int []);
int comprobar_repetir_vector(int, int, int []);
int comprobar_repetir_matriz(int, double, double [][1000]);
void arrancar_motor(int, int);
int suma(double);
double aleatorio(int, int);
void cifrar(int, int, char []);
void descifrar(int, char []);

int main() {

generar_clave(15);
arrancar_motor(15, 1);



}

// comprueba si un numero esta repetido en una matriz, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_matriz(int n, double numero, double matriz[][1000]) {

int x;
int y;
for(x=0; x<n-1; x++) {

for(y=0;y<1000;y++) {

if (numero==matriz[x][y]) {
return 1;
}
}

}
return 0;

}

// comprueba si un numero esta repetido en un vector, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_vector(int n, int numero, int vector[]){

int x;
for(x=0; x<n-1;x++) {

if(numero==vector[x]) {

return 1;

}
}
return 0;
}

// Devuelve la suma de las cifras de un numero
int suma(double numero) {

int resultado=0;
while (numero>0) {

resultado+=fmod(numero, 10);
numero/=10;
}
return resultado;
}

//Genera un numero aleatorio sin decimales dentro de un rango entre max y min.
double aleatorio(int max, int min) {


return rand () % (max-min+1) + min;


}
//Genera una clave optimidada y la guarda en la variable global clave.
void generar_clave(int numero_suma) {


int maximo, minimo, x, y, num_random;

int max=120;
int min=60;

maximo=numero_suma*max/20;
minimo=numero_suma*min/20;


for(x=0;x<numero_de_letras;) {
num_random=aleatorio(maximo, minimo);
if (comprobar_repetir_vector(x, num_random, &clave[0])==1) {
continue;
}
clave[x]=num_random;
x++;
}

printf("se ha generado la clave: \n");
for(y=0;y<numero_de_letras;y++) {

printf("(%d)", clave[y]);

}
printf("\n");
}

// Permite ingresar el valor de cualquier array en la variable global clave.
void ingresar_clave(int array[]) {

int x;

for(x=0;x<numero_de_letras;x++) {

clave[x]=array[x];
}

}
// Genera los numeros aleatorios correspondientes a cada letra y los guarda según corresponda en la matriz claves.
void arrancar_motor(int numero_cifras, int cantidad_numeros){

srand(time(NULL));

int y, z, h;
double num_random;

double min=(double)pow(10.0, numero_cifras-1);
double max=(double)pow(10.0, numero_cifras)-1;

for(z=0; z<numero_de_letras; z++) {
printf("Inicializando letra %c\n", letras[z]);
for(h=0; h<cantidad_numeros;) {
num_random=aleatorio(max, min);
printf("%lf", num_random);
if (((fabs(suma(num_random)-clave[z])) < 0.001) && (comprobar_repetir_matriz(z, num_random, claves))==0) {

claves[z][h]=num_random;
printf("%.0lf\n", num_random);
h++;



} else { continue;  }


}
}
printf("se ha generado la clave: \n");
for(y=0;y<numero_de_letras;y++) {

printf("(%d)", clave[y]);

}
printf("\ncon un numero de cifras de %d\n", numero_cifras);
}

// Cifra un texto usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global cifrado.
void cifrar(int numero_cifras, int cantidad_numeros, char texto[]) {


int letra, letrass, x;
char cifrado_inside[strlen(texto)*numero_cifras];
char string_auxiliar[numero_cifras-1];
for(letra=0; strlen(texto)>letra; letra++) {


for(letrass=0; letrass<numero_de_letras; letrass++) {

if (texto[letra]==letras[letrass]) {
sprintf(string_auxiliar, "%.0lf", claves[letrass][(int)(aleatorio(cantidad_numeros-1, 0))]);
strcat(cifrado_inside, string_auxiliar);
break;
}

}

}
strcpy(cifrado, cifrado_inside);

}

// Descifra un texto cifrado anteriormente usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global descifrado.
void descifrar(int numero_cifras, char cifrado[]) {


char auxiliar[numero_cifras];
int x, y=0, z;

for(x=0; x<strlen(cifrado); x++) {

auxiliar[y]=cifrado[x];
y++;


if(y==numero_cifras) {

for(z=0; z<numero_de_letras; z++) {

if(suma(atof(auxiliar))==clave[z]) {

descifrado[strlen(descifrado)]=letras[z];
y=0;
break;

}

}

}

}

}



Pero ahora está en que el código no compila, el gcc me da el siguiente error:


pruebas: En la función `_fini':
(.fini+0x0): definiciones múltiples de `_fini'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../lib/crti.o:(.fini+0x0): primero se definió aquí
pruebas: En la función `data_start':
(.data+0x0): definiciones múltiples de `__data_start'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../lib/crt1.o:(.data+0x0): primero se definió aquí
pruebas: En la función `data_start':
(.data+0x8): definiciones múltiples de `__dso_handle'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/crtbegin.o:(.data+0x0): primero se definió aquí
pruebas:(.rodata+0x0): definiciones múltiples de `_IO_stdin_used'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../lib/crt1.o:(.rodata.cst4+0x0): primero se definió aquí
pruebas: En la función `_start':
(.text+0x0): definiciones múltiples de `_start'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../lib/crt1.o:(.text+0x0): primero se definió aquí
pruebas: En la función `_init':
(.init+0x0): definiciones múltiples de `_init'
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../lib/crti.o:(.init+0x0): primero se definió aquí
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/crtend.o:(.tm_clone_table+0x0): definiciones múltiples de `__TMC_END__'
pruebas:(.data+0x30): primero se definió aquí
/usr/bin/ld: error en pruebas(.eh_frame); no se creará la tabla .eh_frame_hdr.
collect2: error: ld devolvió el estado de salida 1



No se que estoy haciendo mal la verdad, uso gcc en gnu/linux, exactamente en antergos, y para compilarlo hago un "gcc pruebas -o pruebas.c -lm". He probado a compilarlo con compiladores online para ver si es que era mi pc y no el código, y me da el error:


In function `arrancar_motor':
undefined reference to `pow'
undefined reference to `pow'


En cambio como puedes ver está incluida la librería math.h

¿ Me puedes echar un cable con este error ?, ¿ he implementado bien las corecciones que has descrito en el anterior comentario ?.

Saludos.

T. Collins


pacosn1111

#5
Cita de: T. Collins en 22 Abril 2015, 16:52 PM
Código (bash) [Seleccionar]
gcc pruebas.c -o pruebas -lm

Ostras, que despiste, perdonad, ya lo he compilado y lo he probado, el caso es que si arranco el motor con una clave de 10 cifras arranca perfecto, pero cuando le meto ya 15 o 20 genera un único número aleatorio, este: "-2147483648".

Saludos.

T. Collins

Estás utilizando pow que puede devolver números muy grandes que no entran en cualquier tipo de variable.

pacosn1111

voy avanzando, dejo el codigo que llevo ahora:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>

// constantes

#define numero_de_letras 27

// variables globales

double claves[numero_de_letras][1000];
int clave[numero_de_letras];


// arrays globales
char letras[]="abcdefghijklmnopqrstuvwxyz ";
char cifrado[999999999];
char descifrado[999999999];

// prototipos de funciones
void generar_clave(int);
void ingresar_clave(int []);
int comprobar_repetir_vector(int, int, int []);
int comprobar_repetir_matriz(int, double, double [][1000]);
void arrancar_motor(int, int);
int suma(double);
double aleatorio(double, double);
void cifrar(int, int, char []);
void descifrar(int, char []);

int main() {


generar_clave(10);
arrancar_motor(10, 1);




}

// comprueba si un numero esta repetido en una matriz, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_matriz(int n, double numero, double matriz[][1000]) {

int x;
int y;
for(x=0; x<n-1; x++) {

for(y=0;y<1000;y++) {

if (numero==matriz[x][y]) {
return 1;
}
}

}
return 0;

}

// comprueba si un numero esta repetido en un vector, si esta repetido devuelve 1, en caso contrario 0.
int comprobar_repetir_vector(int n, int numero, int vector[]){

int x;
for(x=0; x<n-1;x++) {

if(numero==vector[x]) {

return 1;

}
}
return 0;
}

// Devuelve la suma de las cifras de un numero
int suma(double numero) {

int resultado=0;
while (numero>0) {

resultado+=fmod(numero, 10);
numero/=10;
}
return resultado;
}

//Genera un numero aleatorio sin decimales dentro de un rango entre max y min.
double aleatorio(double max, double min) {


return fmod(rand (), (max-min+1) + min);


}

//Genera una clave optimidada y la guarda en la variable global clave.
void generar_clave(int numero_suma) {


int maximo, minimo, x, y, num_random;

int max=120;
int min=60;

maximo=numero_suma*max/20;
minimo=numero_suma*min/20;


for(x=0;x<numero_de_letras;) {
num_random=aleatorio(maximo, minimo);
if (comprobar_repetir_vector(x, num_random, &clave[0])==1) {
continue;
}
clave[x]=num_random;
x++;
}

printf("se ha generado la clave: \n");
for(y=0;y<numero_de_letras;y++) {

printf("(%d)", clave[y]);

}
printf("\n");
}

// Permite ingresar el valor de cualquier array en la variable global clave.
void ingresar_clave(int array[]) {

int x;

for(x=0;x<numero_de_letras;x++) {

clave[x]=array[x];
}

}
// Genera los numeros aleatorios correspondientes a cada letra y los guarda según corresponda en la matriz claves.
void arrancar_motor(int numero_cifras, int cantidad_numeros){
   
    srand(getpid());

int y, z, h;
double num_random;

double min=(double)pow(10.0, numero_cifras-1);
double max=(double)pow(10.0, numero_cifras)-1;

for(z=0; z<numero_de_letras; z++) {
printf("Inicializando letra %c\n", letras[z]);
for(h=0; h<cantidad_numeros;) {
num_random=aleatorio(max, min);
                        printf("%lf\n", num_random);
if (((fabs(suma(num_random)-clave[z])) < 0.001) && (comprobar_repetir_matriz(z, num_random, claves))==0) {

claves[z][h]=num_random;
printf("%.0lf\n", num_random);
h++;



} else { continue;  }


}
}
printf("se ha generado la clave: \n");
for(y=0;y<numero_de_letras;y++) {

printf("(%d)", clave[y]);

}
printf("\ncon un numero de cifras de %d\n", numero_cifras);
            printf("%lf, %lf", max, min);
}

// Cifra un texto usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global cifrado.
void cifrar(int numero_cifras, int cantidad_numeros, char texto[]) {


int letra, letrass, x;
char cifrado_inside[strlen(texto)*numero_cifras];
char string_auxiliar[numero_cifras-1];
for(letra=0; strlen(texto)>letra; letra++) {


for(letrass=0; letrass<numero_de_letras; letrass++) {

if (texto[letra]==letras[letrass]) {
sprintf(string_auxiliar, "%.0lf", claves[letrass][(int)(aleatorio(cantidad_numeros-1, 0))]);
strcat(cifrado_inside, string_auxiliar);
break;
}

}

}
strcpy(cifrado, cifrado_inside);

}

// Descifra un texto cifrado anteriormente usando la clave almancenada en la variable global clave, el resultado lo guarda en la variable global descifrado.
void descifrar(int numero_cifras, char cifrado[]) {


char auxiliar[numero_cifras];
int x, y=0, z;

for(x=0; x<strlen(cifrado); x++) {

auxiliar[y]=cifrado[x];
y++;


if(y==numero_cifras) {

for(z=0; z<numero_de_letras; z++) {

if(suma(atof(auxiliar))==clave[z]) {

descifrado[strlen(descifrado)]=letras[z];
y=0;
break;

}

}

}

}

}



Pero ahora el problema es que se generan numeros aleatorios con cifras incorrectas, es decir que si tienen que generarse de 10 cifras, se generan de 10 cifras y alguno que otro de 8 y 9. ¿Podeis echarle un vistazo?.

Saludos