podria ser mas eficiente este codigo ?? consejoss :)

Iniciado por manuchi, 4 Septiembre 2019, 00:18 AM

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

manuchi

***leer una matriz de NxM de enteros, generar un arreglo de structs que almacene para cada fila, el índice de la fila y :

- la suma de los múltiplos de N (en dicha fila) .
- si ni hubiese múltiplos la cantidad de pares (en dicha fila).
- si no hubiese ni múltiplos ni pares, no generar elemento para esa fila.

Nota: implementar funciones que evaluen los resultados pedidos en una fila de la matriz, pasando como argumento la fila como arreglo unidimensional.
Ejemplo: matriz "A" 4x3:
3    1   21
16  5   8
7   11  9
10  7   14

B(arreglo): |1,24|,|3,2|         respuestas

#include <stdio.h>
#include <stdlib.h>
#define M 5

struct arreglo
{
   int indice,suma,par;
};

void lee (int a[][M], int *dn, int *dm);
int sumar (int a[][M], int i, int dn, int dm);
int pares (int a[][M], int i, int dm);
//void esc (int a[][M], int dn, int dm);



int main()
{
   int a[20][M], dn=0, dm=0,i,j,k=0, sum, cont;
   struct arreglo v[10];

   lee(a,&dn,&dm);
   for (i=0;i<dn;i++)
   {
       sum=0;
       sum=sumar (a,i,dn,dm);
       if (sum!=0)
       {
           printf("%d",v[k].indice=i);
           printf("\t%d",v[k].suma=sum);
           k++;
       }
       else{
           cont=0;
           cont=pares(a,i,dm);
           if (cont!=0)
           {
               printf("\n%d",v[k].indice=i);
               printf("\t%d",v[k].par=cont);
               k++;
           }
       }
   printf("\n");
   }
}

void lee(int a[][M], int *dn, int *dm)
{
   int i, j;
   FILE *fp;
   fp=fopen ("numeros.txt","r");
   fscanf(fp,"%d",dn); fscanf (fp,"%d",dm); // leo cant de filas y cant de columnas
   for (i=0;i<*dn;i++)
       for (j=0;j<*dm;j++)
           fscanf(fp,"%d",&a[j]);
   fclose(fp);
}

int sumar(int a[][M], int i, int dn, int dm)
{
   int j, sum=0;
   for (j=0;j<dm;j++)
       if ((a[j]%dn)==0)
           sum+=a[j];
   return sum;
}
int pares(int a[][M], int i, int dm)
{
    int j, cont=0;
    for (j=0;j<dm;j++)
       if ((a[j]%2)==0)
           cont++;
   return cont;
}

K-YreX

El primer consejo (que espero que sea tenido en cuenta) es: poner el código entre etiquetas de código GeSHi de lenguaje C. Yo creo que este es el más importante de todos así que no lo tomes a la ligera si piensas seguir participando en el foro... :silbar:

Aparte del consejo estrella pues se me ocurre alguno más:
  • Nombre de variables mejorables. No hay nada como leer un código y no tener que volver al principio para recordar qué hacía una variable llamada <a> por ejemplo.
  • Asignas dos veces 0 a <sum> de forma innecesaria.
  • Asignas dos veces 0 a <cont> de forma innecesaria.
  • Las asignaciones que hay dentro de los <printf()> no son nada claras de ver.
  • No usar "magic numbers".
  • Primero trabaja con los datos (lee valores, guarda valores, realiza cálculos, etc) y al final muestra los resultados. Así queda más claro el código.

    Luego a mí personalmente me gusta dejar los prototipos sin nombres de parámetros (sólo con el tipo) y poner los nombres únicamente a las implementaciones. Así no tengo que andar recordando que nombres puse para poner los mismos y si hago algún cambio no tengo que acordarme de realizarlo en ambos sitios.
    Y las variables de un <struct> prefiero ponerlas una por línea así se ve más fácil cuántas hay y de qué tipo es cada una.

    Y si quieres alguna mejora de eficiencia ahí va una aunque te parecerá ridícula: usar en los bucles <for> un contador (i/j) de 64 bits ahorra a la hora de generar el código ensamblador una instrucción que se encarga de transformar los índices de 32 bits en índices de 64 bits.

    Yo de momento me fijaría más en los primeros consejos que en este último ya que siempre existirá una lucha entre eficiencia y legibilidad. Un código relativamente pequeño no va a conseguir mucha optimización por este tipo de cambios pero sí puede conseguir que un programador tenga más o menos ganas de leerlo según lo legible que sea. :-X
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;


RayR

#3
Un consejo general sobre la optimización es que no te enfoques tanto en ello mientras estás aprendiendo. Lo conveniente es que te limites a optimizaciones a nivel de diseño, que consisten en elegir los algoritmos y tipos de datos abstractos adecuados para cada problema. Ejemplos son los distintos métodos de ordenamiento y tipos de datos como pilas, listas, árboles, y seguramente te los enseñarán más adelante.

Luego hay otros tipos de optimizaciones que se pueden hacer, pero dependen del lenguaje y la arquitectura del hardware para la que se esté programando. Pueden ser muy tentadoras pero es muy difícil saber realmente si conviene aplicarlas o no (sobre todo en el caso de las optimizaciones dependientes del hardware). Los procesadores actuales son extremadamente complejos, y cuando optimizamos algo, probablemente estamos empeorando otra cosa distinta. Hay tantos factores en juego que casi ninguna optimización es general. Hay que evaluar cada caso por separado.

Un ejemplo es el uso de índices de 64 bits. Es un buen consejo, ya que, efectivamente, con índices de 32 bits se puede requerir alguna instrucción adicional para expandirlos a 64. Pero ojo, que esto sólo aplica si compilamos para 64 bits. Si compilas para 32 bits (y es muy probable, ya que los IDEs de forma predeterminada suelen compilar para esa arquitectura), por más que uses procesador y sistema operativo de 64 bits, el programa se ejecutará en modo 32 bits, por lo que no habrá conversión, y de hecho, dado que en ese modo el procesador no puede realizar operaciones con enteros mayores a 32 bits, al usar datos de 64 bits, el compilador deberá generar instrucciones intermedias para obtener el resultado de 64 bits, haciendo tu código bastante ineficiente. Compilando para 64 bits sí podrías obtener algún beneficio al usar índices de 64 bits, pero ojo, esto aplicaría para índices y poco más. Los índices de 32 bits de expanden a 64 porque el acceso a elementos de un array se hace con aritmética de punteros, que en modo 64 bits ocupan precisamente 64 bits, por lo que los índices deben tener ese tamaño. Para otro tipo de operaciones, las variables de 32 bits no son expandidas (a menos que las  usemos en operaciones combinándolas con datos de 64 bits), por lo que no tendría sentido declararlas como de 64. Y es que, contrario a lo que a veces se lee, los procesadores, al menos los x86-64 (Intel/AMD), en general son al menos igual de eficientes (a veces más eficientes) al operar con 32 bits. Por ejemplo, en un procesador Intel Core de la generación actual, la división entera de datos de 64 bits ocupa 36 ciclos de reloj, mientras que con operandos de 32 bits sólo ocupa 10 ciclos. Esto trabajando en modo 64 bits. Luego hay otros factores a tener en cuenta, como el hecho de que los datos más grandes llenan más rápido los buses y cachés, pero no nos compliquemos. El caso es que es bastante complejo determinar el costo/beneficio. Por eso, mejor, de momento al menos, limitarse a las optimizaciones a nivel de diseño/algoritmo, que son más generales.

Por cierto, el tipo de datos recomendado para índices es el size_t, que ocupará 32 o 64 bits en función de la arquitectura para la que compiles, por lo que si declaras así tus índices, siempre se usará el tamaño "óptimo".