Domanda Ottimizzare in C - Consigli

k35p0n33

Utente Iron
22 Febbraio 2022
16
4
2
18
Ultima modifica:
Salve ragazzi, ho appena svolto un esercizio, volevo chiedervi qualche consiglio per velocizzare il ragionamento e ottimizzare il codice quando scrivo un esercizio. Voi avete qualche metodo? Per esempio la stesura di una Flow Chart prima di scrivere il codice?
L'esercizio in questione è il seguente:
C:
/*
 Scrivere un programma che definisca una matrice di valori interi e di dimensioni richieste di volta in volta dall’utente (massimo 10x10) mediante input quali “quante righe?” e “quante colonne?”. Successivamente di tutti questi valori determini il massimo, il minimo, la somma e la media (frazionaria).
 */
#include <stdlib.h>
#include <stdio.h>
#define MAX 10

int main() {
    int m[MAX][MAX]={{0},{0}};
    int r,c,i=0,j=0,max=0,sum=0,min;
    float media;
   
    printf("Crea la tua matrice!\n MAX 10x10!\n");
    printf("Quante righe?=");
    scanf("%d", &r);
    if(r<10 && r>0)
        printf("Dato inserito correttamente\n");
    if(r>10){
        printf("Dato non valido - MAX 10\n");
        return 1;
    }
   
   
    printf("Quante colonne?=");
    scanf("%d", &c);
    if(c<10 && c>0)
        printf("Dato inserito correttamente\n");
    if(c>10){
        printf("Dato non valido - MAX 10\n");
        return 1;
    }
    // Riempimento matrice - Elaborazione dati
    min=50;
    printf("Inserisci i valori nella matrice!\n");
    for(i=0;i<r;i++){
        for(j=0;j<c;j++){
            printf("Valore %d.%d = ", i,j);
            scanf("%d", &m[i][j]);
            sum=sum+m[i][j];
            if(m[i][j]>max){
                max=m[i][j];
            }
           
            if(m[i][j]<min){
                min=m[i][j];
            }
        }
    }
   
    // Stampa della matrice
    printf("LA TUA MATRICE:\n");
    for(i=0;i<r;i++){
        for(j=0;j<c;j++){
            printf("%d ", m[i][j]);
        }
        printf("\n");
    }
    media=(float)sum/r*c;
    printf("La somma è %d\n", sum);
    printf("Il valore max è %d\n", max);
    printf("Il valore min è %d\n", min);
    printf("La media è %f\n", media);
   

    return 0;
}

Attendo alcuni "tips and tricks" da parte vostra, grazie.
 
In questo codice non c'è molto da ottimizzare. A livello logico e di progettazione del codice se puoi risparmiare tempo della CPU e memoria avendo gli stessi risultati è bene ma bisogna valutare anche quanto ti costa in termini di sviluppo sia per tempo che per quantità di codice. In facoltà di ingegneria (che non ho frequentato) si parla spesso di Big O notation, l'esempio accademico classico di ottimizzazione puoi trovarlo nel paragone tra una ricerca lineare e una ricerca binaria su dati ordinati, nel primo hai nel peggior caso una complessità O(n) che prende sempre più tempo più gli elementi aumentano, nel secondo O(log n) che riesce a trovare un valore tra miliardi con una manciata di controlli. Tuttavia nella pratica quando scrivi codice non è necessario fare calcoli di complessità se non stai programmando i controlli della stazione spaziale o un dispositivo con poca capacità di batteria, ti basta cercare di riassumere il risultato che vuoi ottenere in codice togliendo passaggi superflui, poi l'esperienza ti farà essere più rapido ed individuare questi passaggi a colpo d'occhio.

Poi il compilatore di C ti aiuta, l'ottimizzazione è una parte importantissima e la sua implementazione è tra i codici più complessi del mondo, infatti se vai ad analizzare il binario prodotto da un tuo sorgente vedrai che molte delle istruzioni sono state stravolte perché il compilatore ha trovato un modo più efficiente per farlo a basso livello (ovviamente il senso rimane quello, non ti sostituirà la ricerca lineare con quella binaria, ma cercherà di velocizzare quella lineare in base al contesto).
 
  • Grazie
Reazioni: k35p0n33
In questo codice non c'è molto da ottimizzare. A livello logico e di progettazione del codice se puoi risparmiare tempo della CPU e memoria avendo gli stessi risultati è bene ma bisogna valutare anche quanto ti costa in termini di sviluppo sia per tempo che per quantità di codice. In facoltà di ingegneria (che non ho frequentato) si parla spesso di Big O notation, l'esempio accademico classico di ottimizzazione puoi trovarlo nel paragone tra una ricerca lineare e una ricerca binaria su dati ordinati, nel primo hai nel peggior caso una complessità O(n) che prende sempre più tempo più gli elementi aumentano, nel secondo O(log n) che riesce a trovare un valore tra miliardi con una manciata di controlli. Tuttavia nella pratica quando scrivi codice non è necessario fare calcoli di complessità se non stai programmando i controlli della stazione spaziale o un dispositivo con poca capacità di batteria, ti basta cercare di riassumere il risultato che vuoi ottenere in codice togliendo passaggi superflui, poi l'esperienza ti farà essere più rapido ed individuare questi passaggi a colpo d'occhio.

Poi il compilatore di C ti aiuta, l'ottimizzazione è una parte importantissima e la sua implementazione è tra i codici più complessi del mondo, infatti se vai ad analizzare il binario prodotto da un tuo sorgente vedrai che molte delle istruzioni sono state stravolte perché il compilatore ha trovato un modo più efficiente per farlo a basso livello (ovviamente il senso rimane quello, non ti sostituirà la ricerca lineare con quella binaria, ma cercherà di velocizzare quella lineare in base al contesto).
Grazie mille, io attualmente sto frequentando il primo anno di ingegneria informatica ed infatti ciò che ci dicono sempre è che per imparare a programmare bisogna programmare per l'appunto (fare pratica). Ora le chiedo: per lo sviluppo di algoritmi e codici più complessi le è utile svolgere qualche bozza su carta o qualsiasi altro schema prima di scriverlo sull'IDE? O semplicemente scrive direttamente? Avete qualche consiglio/metodo per organizzare al meglio la scrittura del codice in modo da non fare confusione?
 
Dammi pure del tu, ci scambiamo di qualche anno. Personalmente mi è utile scrivere bozze su carta quando progetto basi di dati, interfacce grafiche, infrastrutture di rete e protocolli. Per il resto faccio uno schema mentale e poi vado direttamente sul codice ma questa è una cosa molto personale ognuno ha il metodo con cui è più produttivo quindi ti consiglio di provarli un po' tutti e decidere da solo con quale ti trovi meglio.
 
  • Grazie
Reazioni: k35p0n33
Dammi pure del tu, ci scambiamo di qualche anno. Personalmente mi è utile scrivere bozze su carta quando progetto basi di dati, interfacce grafiche, infrastrutture di rete e protocolli. Per il resto faccio uno schema mentale e poi vado direttamente sul codice ma questa è una cosa molto personale ognuno ha il metodo con cui è più produttivo quindi ti consiglio di provarli un po' tutti e decidere da solo con quale ti trovi meglio.
Grazie mille, essendo all'inizio ancora non ho un approccio standard e solido per cui quando scrivo un codice sto cercando di sperimentare e provare più metodi possibili. Ritengo prezioso ricevere consigli da persone che ne masticano di più come te. Grazie per la disponibilità :D
 
  • Mi piace
Reazioni: JunkCoder
Non proprio ottimizzazione, ma cose del tipo:
C:
if(r<10 && r>0)
    printf("Dato inserito correttamente\n");
if(r>10){
    printf("Dato non valido - MAX 10\n");
    return 1;
}
vengono meglio se scritte nella forma:
C:
if (!(r<10 && r>0)) {
    fprintf(stderr, "Dato non valido - MAX 10\n");
    return 1;
}

printf("Dato inserito correttamente\n");
e io eviterei di scrivere "Dato inserito correttamente" seguendo la filosofia che se non hai messaggi di errori allora non stai facendo niente di sbagliato.

In ANSI C le variabili devono essere definite all'inizio di ogni blocco, ma se hai la possibilità di seguire uno standard più recente (tipo quello di 23 anni fa) è meglio definirle vicino a dove andrai ad usarle. Gli operatori * e / hanno la stessa precedenza quindi media=(float)sum/r*c; è proprio sbagliato e va scritto con le parentesi media=(float)sum/(r*c);.

Ti posto il codice sistemato a modo mio ma, come ha detto JunkCoder, la verità è che nel tuo codice non c'è proprio niente da sistemare (se non per la media).
C:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX 10

int main() {
  int m[MAX][MAX];
  printf("Crea la tua matrice!\n MAX 10x10!\n");

  int r;
  printf("Quante righe?=");
  scanf("%d", &r);

  int c;
  printf("Quante colonne?=");
  scanf("%d", &c);

  if (!(r < 10 && r > 0) || !(c < 10 && c > 0)) {
    fprintf(stderr, "Dimensioni non valide\n");
    return 1;
  }

  printf("Inserisci i valori nella matrice!\n");
  int min = INT_MAX, max = INT_MIN, sum = 0;
  for (int i = 0; i < r; i++) {
    for (int j = 0; j < c; j++) {
      printf("Valore %d.%d = ", i, j);
      scanf("%d", &m[i][j]);
      sum += m[i][j];
      if (m[i][j] > max) max = m[i][j];
      if (m[i][j] < min) min = m[i][j];
    }
  }

  printf("LA TUA MATRICE:\n");
  for (int i = 0; i < r; i++) {
    for (int j = 0; j < c; j++)
      printf("%d ", m[i][j]);
    printf("\n");
  }

  float media = (float)sum / (r * c);
  printf("La somma è %d\n", sum);
  printf("Il valore max è %d\n", max);
  printf("Il valore min è %d\n", min);
  printf("La media è %f\n", media);

  return 0;
}

Ora le chiedo: per lo sviluppo di algoritmi e codici più complessi le è utile svolgere qualche bozza su carta o qualsiasi altro schema prima di scriverlo sull'IDE? O semplicemente scrive direttamente? Avete qualche consiglio/metodo per organizzare al meglio la scrittura del codice in modo da non fare confusione?
Quando si tratta di algoritmi complessi, dove è facile sbagliarsi, devi avere ben chiaro cosa stai facendo prima di metterti a scrivere codice. Prendi ad esempio quicksort. Sei al primo anno quindi non mi aspetto che tu lo conosca, ma eventualmente ti capiterà di studiarlo e col passare degli anni non ti ricorderai più il codice (o lo pseudocodice) a memoria ma (se ti è piaciuto) ti ricorderai il funzionamento dell'algoritmo. Non è un algoritmo difficile, ma è facile sbagliarsi con i vari +1 e -1 o con i vari >= invece di >. Se ti dovessi chiedere di svilupparlo in python magari sarai in grado di scrivere direttamente il codice e saprai risolvere gli eventuali errori con facilità, ma se ti dovessi chiedere di scriverlo in assembly la storia è un po' diversa.

Chiamala bozza su carta o chiamala codifica in un linguaggio ad alto livello (o pseudocodice), ma prima di metterti a scrivere codice un po' intricato in un linguaggio che non padroneggi benissimo è molto utile farsi qualche schema da ricalcare. Per riportarci all'esempio di quicksort, io mi farei uno schemino grafico di un array per capire bene dove iniziano e dove finiscono i vari boundaries: non è un flowchart, non è uno pseudocodice e non è uno schema standardizzato, ma è comunque un modo per far lavorare il cervello prima di metterti a battere le tue insicurezze sulla tastiera.
 
Ultima modifica:
Non proprio ottimizzazione, ma cose del tipo:
C:
if(r<10 && r>0)
    printf("Dato inserito correttamente\n");
if(r>10){
    printf("Dato non valido - MAX 10\n");
    return 1;
}
vengono meglio se scritte nella forma:
C:
if (!(r<10 && r>0)) {
    fprintf(stderr, "Dato non valido - MAX 10\n");
    return 1;
}

printf("Dato inserito correttamente\n");
e io eviterei di scrivere "Dato inserito correttamente" seguendo la filosofia che se non hai messaggi di errori allora non stai facendo niente di sbagliato.

In ANSI C le variabili devono essere definite all'inizio di ogni blocco, ma se hai la possibilità di seguire uno standard più recente (tipo quello di 23 anni fa) è meglio definirle vicino a dove andrai ad usarle. Gli operatori * e / hanno la stessa precedenza quindi media=(float)sum/r*c; è proprio sbagliato e va scritto con le parentesi media=(float)sum/(r*c);.

Ti posto il codice sistemato a modo mio ma, come ha detto JunkCoder, la verità è che nel tuo codice non c'è proprio niente da sistemare (se non per la media).
C:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX 10

int main() {
  int m[MAX][MAX];
  printf("Crea la tua matrice!\n MAX 10x10!\n");

  int r;
  printf("Quante righe?=");
  scanf("%d", &r);

  int c;
  printf("Quante colonne?=");
  scanf("%d", &c);

  if (!(r < 10 && r > 0) || !(c < 10 && c > 0)) {
    fprintf(stderr, "Dimensioni non valide\n");
    return 1;
  }

  printf("Inserisci i valori nella matrice!\n");
  int min = INT_MAX, max = INT_MIN, sum = 0;
  for (int i = 0; i < r; i++) {
    for (int j = 0; j < c; j++) {
      printf("Valore %d.%d = ", i, j);
      scanf("%d", &m[i][j]);
      sum += m[i][j];
      if (m[i][j] > max) max = m[i][j];
      if (m[i][j] < min) min = m[i][j];
    }
  }

  printf("LA TUA MATRICE:\n");
  for (int i = 0; i < r; i++) {
    for (int j = 0; j < c; j++)
      printf("%d ", m[i][j]);
    printf("\n");
  }

  float media = (float)sum / (r * c);
  printf("La somma è %d\n", sum);
  printf("Il valore max è %d\n", max);
  printf("Il valore min è %d\n", min);
  printf("La media è %f\n", media);

  return 0;
}


Quando si tratta di algoritmi complessi, dove è facile sbagliarsi, devi avere ben chiaro cosa stai facendo prima di metterti a scrivere codice. Prendi ad esempio quicksort. Sei al primo anno quindi non mi aspetto che tu lo conosca, ma eventualmente ti capiterà di studiarlo e col passare degli anni non ti ricorderai più il codice (o lo pseudocodice) a memoria ma (se ti è piaciuto) ti ricorderai il funzionamento dell'algoritmo. Non è un algoritmo difficile, ma è facile sbagliarsi con i vari +1 e -1 o con i vari >= invece di >. Se ti dovessi chiedere di svilupparlo in python magari sarai in grado di scrivere direttamente il codice e saprai risolvere gli eventuali errori con facilità, ma se ti dovessi chiedere di scriverlo in assembly la storia è un po' diversa.

Chiamala bozza su carta o chiamala codifica in un linguaggio ad alto livello (o pseudocodice), ma prima di metterti a scrivere codice un po' intricato in un linguaggio che non padroneggi benissimo è molto utile farsi qualche schema da ricalcare. Per riportarci all'esempio di quicksort, io mi farei uno schemino grafico di un array per capire bene dove iniziano e dove finiscono i vari boundaries: non è un flowchart, non è uno pseudocodice e non è uno schema standardizzato, ma è comunque un modo per far lavorare il cervello prima di metterti a battere le tue insicurezze sulla tastiera.
Grazie di cuore St3ve, andrò a studiarmi questo quicksort dato che non lo conosco. Ho visto il codice che hai riscritto ed effettivamente cambia poco, infatti devo prendere l'abitudine a compattare il codice come sum=sum+m[i][j]; in sum+=m[i][j]; (come hai scritto tu) alla fine non è niente di ché e cambia davvero pochissimo quindi è una questione soggettiva però mi piace scrivere in maniera più ordinata possibile in modo da avere una facile comprensione quando scriverò codici più complessi e lunghi. Ringrazio nuovamente entrambi per le preziose informazioni! :)
 
Ultima modifica:
Ciao, io quando sviluppo seguo questa procedura, forse può esserti utile, con carta e penna:
-Analizzo i dati in input e output
-Scelgo una strategia risolutiva (algoritmo)
-Scrivo il flow chart
-Scelgo l'opportuna struttura di dati per risolvere il problema (es. se devo gestire collezioni di dati uguali, in C++, uso Array. Per gestire dati complessi, uso i record)
-Traduco il tutto in linguaggio di programmazione

Poi mi piacerebbe darti un piccolo "trucco" che si usa per convalidare i dati: tu hai usato l'if, mentre spesso è conveniente usare il while con all'interno la condizione violata della convalida, perché non sappiamo quante volte l'utente possa sbagliare l'input. Esempio, devo convalidare un numero compreso tra 1 e 7 allora scrivo qualcosa del genere ( scrivo in C++, facciamo finta che ho già dichiarato la variabile):
C++:
while (numero < 1 || numero > 7){
    cout << "Errore" << endl;
    cout << "Inserire numero valido:" << endl;
    cin >> numero;
}
 
Ciao, io quando sviluppo seguo questa procedura, forse può esserti utile, con carta e penna:
-Analizzo i dati in input e output
-Scelgo una strategia risolutiva (algoritmo)
-Scrivo il flow chart
-Scelgo l'opportuna struttura di dati per risolvere il problema (es. se devo gestire collezioni di dati uguali, in C++, uso Array. Per gestire dati complessi, uso i record)
-Traduco il tutto in linguaggio di programmazione

Poi mi piacerebbe darti un piccolo "trucco" che si usa per convalidare i dati: tu hai usato l'if, mentre spesso è conveniente usare il while con all'interno la condizione violata della convalida, perché non sappiamo quante volte l'utente possa sbagliare l'input. Esempio, devo convalidare un numero compreso tra 1 e 7 allora scrivo qualcosa del genere ( scrivo in C++, facciamo finta che ho già dichiarato la variabile):
while (numero < 1 || numero > 7){
cout << "Errore" << endl;
cout << "Inserire numero valido:" << endl;
cin >> numero
}
Grazie per avermi rimembrato il while che ci aiuta proprio quando non sappiamo le ripetizioni che l'utente può fare. Purtroppo il ciclo for mi ha viziato :blabla: quindi ho l'abitudine di usare solo lui dimenticando il while. Grazie mille anche per aver condiviso il tuo procedimento, provero a fare lo stesso!
 
  • Mi piace
Reazioni: --- Ra ---