Problemino in C
Ciao a tutti,
vorrei chiedervi gentilmente qualche delucidazione in merito ad un programmino banale che dovrei realizzare.
L'esercizio recita:
"Scrivere un programma in grado di riconoscere e di stampare
tutti i numeri perfetti minori di un limite massimo (lmax)
definito dall’utente. N è un numero perfetto se la somma dei
suoi divisori propri è pari al numero stesso"
Ora l'algoritmo che ho cercato di costruire è il seguente:
- leggere lmax
- Prendere ogni numero naturale i<=lmax
- Trovare per ogni "i" i divisori proprio
- divido "i" per ogni numero naturale "j" compreso tra 1 e "i"
- se il resto della divisione intera è=0 allora chiamo divisore il numero j
- sommo i divisori
- se la somma dei divisori è = "i" allora i=numero perfetto
- stampo numero perfetto.
Il codice che ho realizzato è il seguente:
Ovviamente non ottengo il risultato sperato, ma ricontrollando e provando ad eseguire a mente mi sembrerebbe corretto....dove sbaglio?
grazie
vorrei chiedervi gentilmente qualche delucidazione in merito ad un programmino banale che dovrei realizzare.
L'esercizio recita:
"Scrivere un programma in grado di riconoscere e di stampare
tutti i numeri perfetti minori di un limite massimo (lmax)
definito dall’utente. N è un numero perfetto se la somma dei
suoi divisori propri è pari al numero stesso"
Ora l'algoritmo che ho cercato di costruire è il seguente:
- leggere lmax
- Prendere ogni numero naturale i<=lmax
- Trovare per ogni "i" i divisori proprio
- divido "i" per ogni numero naturale "j" compreso tra 1 e "i"
- se il resto della divisione intera è=0 allora chiamo divisore il numero j
- sommo i divisori
- se la somma dei divisori è = "i" allora i=numero perfetto
- stampo numero perfetto.
Il codice che ho realizzato è il seguente:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { /* Scrivere un programma in grado di riconoscere e di stampare tutti i numeri perfetti minori di un limite massimo (lmax) definito dall’utente. N è un numero perfetto se la somma dei suoi divisori propri è pari al numero stesso N.*/ int limite,j,i,divisore,somma,perfetto; printf("Inserire il limite numerico:\n"); scanf("%d", &limite); i=1; j=1; somma=0; while(i<=limite){ while(j<=i){ if(i%j==0){ divisore=j; }else{ divisore=0; } somma=somma+divisore; j=j+1; } if(somma==i){ perfetto=i; printf("%d",perfetto); } i=i+1; j=1; somma=0; } system("PAUSE"); return 0; }
Ovviamente non ottengo il risultato sperato, ma ricontrollando e provando ad eseguire a mente mi sembrerebbe corretto....dove sbaglio?
grazie
Risposte
Salve ELWOOD,
ammetto che ho un mio modo di programmare e che non tocco la programmazione da più di un anno, e personalmente non condivido l'impostazione del tuo algoritmo (trovo difficoltà a leggerlo)... questo esercizio lo avevo fatto nella prima parte della programmazione, infatti non troverai nell'algoritmo, che posterò, complicati costrutti:
Funziona benissimo, l'ho appena testato su code::blocks....
Attento al valore di n poiché è dichiarato int... se devi inserire un valore più alto del range del tipo int allora devi dichiararlo diversamente.....
Ribadisco che può essere modificato l'algoritmo rendendolo più efficiente, infatti stampa su console, in un tempo ragionevole, i numeri perfetti minori di 10000......
L'ho provato anche su server con il supporto di un ingegnerie informatico presso la facoltà ed mi stampa, in un tempo molto ragionevole, i numeri perfetti minori di 200000000000... ma penso che lo scopo del problema non è quello di stampare tutti i numeri perfetti quanto invece di capire l'algoritmo ed imparare a crearne di nuovi utilizzando il linguaggio C.....
Buon confronto o consultazione!
Cordiali saluti
ammetto che ho un mio modo di programmare e che non tocco la programmazione da più di un anno, e personalmente non condivido l'impostazione del tuo algoritmo (trovo difficoltà a leggerlo)... questo esercizio lo avevo fatto nella prima parte della programmazione, infatti non troverai nell'algoritmo, che posterò, complicati costrutti:
#include <stdio.h> int n,somma,i,k,flag; double f,d; void main (void) { //TITOLO PROGRAMMA printf("COMPITO DI PROGRAMMAZIONE"); printf("\n"); printf("1 MARZO 2011, N 1"); printf("\n"); printf("\n"); printf("CALCOLO DEI NUMERI PERFETTI ENTRO"); printf("\n"); printf("UN INTERVALLO DEL TIPO [0;n]"); printf("\n"); printf("\n"); /* INSERIMENTO DA TASTIERA DEL VALORE DI n */ do { printf("INSERISCI UN NUMERO INTERO PARI"); printf("\n"); printf("MAGGIORE DI ZERO PER FORMARE UN "); printf("\n"); printf("INTERVALLO DEL TIPO [0;n], n= "); scanf("%d", &n); f=n%2; printf("\n"); if(f!=0 || n<=0) { printf("REINSERISCI VALORE DI n"); printf("\n"); printf("SECONDO LE CONDIZIONI"); printf("\n"); printf("SOPRA INDICATE"); printf("\n"); printf("\n"); } } while(f!=0 || n<=0); //CALCOLO DEI NUMERI PERFETTI /* I NUMERI PERFETTI SONO QUEI NUMERI LA CUI SOMMA DEI SUOI DIVISORI ESCLUSO SE STESSO DANNO IL NUMERO IN ESAME */ /* I DIVISORI DI UN NUMERO SONO TUTTI QUEI INTERI MINORI O UGUALI AL NUMERO E MAGGIORI O UGUALI A 1 TALI CHE PRESO IL NUMERO IN ESAME E DIVISO PER QUESTI DIA RESTO ZERO. */ for(i=n; i>=1; i--) { somma=0; for(k=n/2; k>=1; k--) { d=i%k; if(d==0 && i!=k) { somma=somma+k; } } if(somma==i) { /* STAMPA DEGLI EVENTUALI NUMERI PERFETTI */ flag=1; printf("%d E' UN NUMERO PERFETTO", i); printf("\n"); } } if(flag!=1) { printf("NELL'INTERVALLO [0;%d] NON VI SONO NUMERI PERFETTI",n); printf("\n"); } }
Funziona benissimo, l'ho appena testato su code::blocks....
Attento al valore di n poiché è dichiarato int... se devi inserire un valore più alto del range del tipo int allora devi dichiararlo diversamente.....
Ribadisco che può essere modificato l'algoritmo rendendolo più efficiente, infatti stampa su console, in un tempo ragionevole, i numeri perfetti minori di 10000......


Buon confronto o consultazione!
Cordiali saluti
X garnak.olegovitc : Il programma funzione ma secondo gli ultimi standard dovresti usare int come valore di ritorno dell'int. Puoi invece tranquillamente ignorare gli argomenti del main. L'uso di variabili locali è in genere da preferire a variabili globali.
Per un algoritmo alternativo vi suggerisco di studiarti questo:
è però limitato a numeri inferiori di 300000 (un numero scelto abbastanza a caso). Si può adattare a numeri arbitrariamente grandi con un minimo di lavoro.
P.S: Per calcoli più rapidi e che non necessitino particolare memoria si può anche considerare solo i numeri della forma \(2^n(2^{n+1}-1)\) e testare poi che \(2^{n+1}-1\) è primo. I numeri da testare, anche dovendo testare tutti i numeri minori di un 64 bit sono davvero pochi.
Per un algoritmo alternativo vi suggerisco di studiarti questo:
#include<stdio.h> int main () { unsigned long n, i, j; unsigned int flag = 0; unsigned long numb[300000]; unsigned long count = 0; do { printf("Inserisci il limire superiore cercato\n"); scanf("%lu", &n); if(n > 300000) printf("Il numero inserito è troppo grande\n"); else if (n <= 1) printf("Il numero inserito è troppo piccolo\n"); else flag = 1; } while(flag == 0); for(i = 0; i < n; ++i) { numb[i] = 1; } for(i = 1; i < n; ++i) { unsigned int ipp = i+1; if(numb[i] == ipp) { ++count; printf("Il %lu numero perfetto è: %lu\n", count, ipp); } for(j = i+ipp; j < n; j+=ipp) { numb[j]+=ipp; } } if(count == 0) printf("Non ci sono numeri perfetti nell'intervallo considerato\n"); return 0; }
è però limitato a numeri inferiori di 300000 (un numero scelto abbastanza a caso). Si può adattare a numeri arbitrariamente grandi con un minimo di lavoro.
P.S: Per calcoli più rapidi e che non necessitino particolare memoria si può anche considerare solo i numeri della forma \(2^n(2^{n+1}-1)\) e testare poi che \(2^{n+1}-1\) è primo. I numeri da testare, anche dovendo testare tutti i numeri minori di un 64 bit sono davvero pochi.
Salve vict85,
quoto pienamente, il miglioramente al quale mi riferivo era proprio questo...
Per quanto riguarda il void main (void), hai ragione
, sai quante volte fui criticato in merito perchè, è vero, la comunita informatica ha ripudiato quel modo di scrivere ma fino a quando vi sono compilatori che lo accettano io, con tutta l'ammirazione nei tuoi confronti, preferisco scrivere in quel modo
(almeno per essere coerente quando scrivo "Programma in C", il costrutto che proporni tu è del C++... e purtroppo a me il C++ non piace molto, preferisco l'objective C ed il C#)
Saluti garnak.olegovitc
P.S.=Aspetto con ansia un tuo programma adattato...
"vict85":
X garnak.olegovitc : Il programma funzione ma secondo gli ultimi standard dovresti usare int come valore di ritorno dell'int. Puoi invece tranquillamente ignorare gli argomenti del main. L'uso di variabili locali è in genere da preferire a variabili globali.
Per un algoritmo alternativo vi suggerisco di studiarti questo:
#include<stdio.h> int main () { unsigned long n, i, j; unsigned int flag = 0; unsigned long numb[300000]; unsigned long count = 0; do { printf("Inserisci il limire superiore cercato\n"); scanf("%lu", &n); if(n > 300000) printf("Il numero inserito è troppo grande\n"); else if (n <= 1) printf("Il numero inserito è troppo piccolo\n"); else flag = 1; } while(flag == 0); for(i = 0; i < n; ++i) { numb[i] = 1; } for(i = 1; i < n; ++i) { unsigned int ipp = i+1; if(numb[i] == ipp) { ++count; printf("Il %lu numero perfetto è: %lu\n", count, ipp); } for(j = i+ipp; j < n; j+=ipp) { numb[j]+=ipp; } } if(count == 0) printf("Non ci sono numeri perfetti nell'intervallo considerato\n"); return 0; }
è però limitato a numeri inferiori di 300000 (un numero scelto abbastanza a caso). Si può adattare a numeri arbitrariamente grandi con un minimo di lavoro. Al limite metterò dopo il codice adattato.
quoto pienamente, il miglioramente al quale mi riferivo era proprio questo...



Per quanto riguarda il void main (void), hai ragione









Saluti garnak.olegovitc
P.S.=Aspetto con ansia un tuo programma adattato...

Invece di quello metto quello che usa le potenze di 2...
Senza dubbio è assolutamente istantaneo anche se devi calcolarti tutti quelli contenuti in un 64bit...
È banalmente un codice che non ha senso che l'autore del post usi come "soluzione" del compito assegnato. L'altro mio codice va meglio ma tutto sommato il professore aveva in mentre qualcosa del tipo proposto da garnak.
Per chi non lo sapesse:
equivale ad un modo veloce per \(\displaystyle x\cdot 2^n \)
Equivalentemente:
equivale ad un modo veloce per \(\displaystyle x/ 2^n \) (
il resto della divisione viene scartato)

#include<stdio.h> int main () { unsigned long long n, perf, i, j, jlim; unsigned long long ex2, p; unsigned int flag = 0, is_perfect; unsigned long long count = 0; do { printf("Inserisci il limire superiore cercato\n"); scanf("%llu", &n); if (n <= 1) printf("Il numero inserito è troppo piccolo\n"); else flag = 1; } while(flag == 0); i = 1; jlim = 1 << ((i+2)>>1); ex2 = (1<<i); p = (ex2<<1)-1; perf = ex2*p; while(perf < n) { is_perfect = 1; for(j = 3; j < jlim; j+=2) if(p % j == 0) is_perfect = 0; if(is_perfect) { ++count; printf("Il %llu numero perfetto è: %llu\n", count, perf); } ++i; jlim = (1 << ((i+2)>>1)); ex2 = (ex2 << 1); p = (ex2<<1)-1; perf = ex2*p; } if(count == 0) printf("Non ci sono numeri perfetti nell'intervallo considerato\n"); return 0; }
È banalmente un codice che non ha senso che l'autore del post usi come "soluzione" del compito assegnato. L'altro mio codice va meglio ma tutto sommato il professore aveva in mentre qualcosa del tipo proposto da garnak.
Per chi non lo sapesse:
x<<n
equivale ad un modo veloce per \(\displaystyle x\cdot 2^n \)
Equivalentemente:
x>>n
equivale ad un modo veloce per \(\displaystyle x/ 2^n \) (

Salve vict85,
wonderful! Ancora meglio, bravo! Anche se nel compito il professore dovrebbe almeno dare una qualche informazioni in più in merito ai numeri perfetti
....
Sempre se lo sappia!!!
Cordiali saluti
wonderful! Ancora meglio, bravo! Anche se nel compito il professore dovrebbe almeno dare una qualche informazioni in più in merito ai numeri perfetti











Cordiali saluti
"ELWOOD":
Il codice che ho realizzato è il seguente:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { /* Scrivere un programma in grado di riconoscere e di stampare tutti i numeri perfetti minori di un limite massimo (lmax) definito dall’utente. N è un numero perfetto se la somma dei suoi divisori propri è pari al numero stesso N.*/ int limite,j,i,divisore,somma,perfetto; printf("Inserire il limite numerico:\n"); scanf("%d", &limite); i=1; j=1; somma=0; while(i<=limite){ while(j<=i){ if(i%j==0){ divisore=j; }else{ divisore=0; } somma=somma+divisore; j=j+1; } if(somma==i){ perfetto=i; printf("%d",perfetto); } i=i+1; j=1; somma=0; } system("PAUSE"); return 0; }
Tornando al tuo codice, direi che è abbastanza corretto anche se lo stile è un po' da mettere a posto.
L'unico errore è che nei divisori non devi considerare i... Quindi andava un j
A parte questo direi che una indentazione un po' migliore sarebbe auspicabile. Inoltre l'utilizzo del system("PAUSE") è legato ai limiti dell'ide che stai usando (anche se non è l'unico ad averne bisogno).
Per quanto riguardo lo stile, perché non usi for?
Tutto il tuo codice centrale si riscriveva come
for(i = 2; i<limite; i = i+1) { somma=0; for(j=1; j<i; j = j+1) { if(i%j == 0) { somma = somma + j; } } if(somma == i) { printf("%d\n", i); } }
Come vedi ho eliminato un po' di uso inutile della memoria. A parte questo avresti dovuto andare a capo quando stampavi a video il numero.
Grazie mille a tutti ragazzi! 
Si vict85 hai ragione riguardo all'indentazione, ma l'ho copiato da un file txt quindi mi è sparita.
Avrei un'altra piccola domanda (non so se è il caso di aprire un altro thread) Quello che mi chiedo è:
come posso leggere da tastiera un testo comprensivo di numeri?
Io ho utilizzato il comando gets() ma poi nell'eseguire il codice, se inserisco dei numeri mi da errore....come mai?
Questo è il codice di un esecizio, il problema riguarda la lettura di indirizzo che andrebbe scritto con la via e il numero 5 (es: via Dolomiti 9)
appena inserito il valore 9 il programma mi rimanda alla fine del primo ciclo while() come se vi fosse un errore

Si vict85 hai ragione riguardo all'indentazione, ma l'ho copiato da un file txt quindi mi è sparita.
Avrei un'altra piccola domanda (non so se è il caso di aprire un altro thread) Quello che mi chiedo è:
come posso leggere da tastiera un testo comprensivo di numeri?
Io ho utilizzato il comando gets() ma poi nell'eseguire il codice, se inserisco dei numeri mi da errore....come mai?
Questo è il codice di un esecizio, il problema riguarda la lettura di indirizzo che andrebbe scritto con la via e il numero 5 (es: via Dolomiti 9)
#include <stdio.h> #include <stdlib.h> #define N_CASE 20 #define N 50 int main(int argc, char *argv[]) { typedef char stringa[N]; typedef enum{acquisto,vendita}ttipologia; typedef struct{ int costo; float metri_quadri; stringa indirizzo[N]; stringa nome_citta; ttipologia tipologia; }tcasa; typedef tcasa elenco[N_CASE]; elenco casa; int i=0,t; int comando=1; while(i<N_CASE && comando==1){ printf("\t************ IMMOBIL DREAM ***********\n\n"); printf("\t CASA nr.%d\n\n",i+1); printf("Inserire il costo della casa:\n"); scanf("%d",&casa[i].costo); printf("Inserire la superficie della casa:\n"); scanf("%f",&casa[i].metri_quadri); printf("Inserire l'indirizzo:\n"); gets(casa[i].indirizzo); printf("Inserire il nome della citta:\n"); scanf("%s",casa[i].nome_citta); printf("Inserire la tipologia della casa:\n"); scanf("%d",&casa[i].tipologia); while(casa[i].tipologia!=0 && casa[i].tipologia!=1){ printf("Il valore deve essere 0=acquisto,oppure 1=vendita.\nImmetti un nuovo valore per la tipologia:\n"); scanf("%d",&casa[i].tipologia); } printf("Terminare l'inserimento? SI=0 NO=1:\n"); scanf("%d",&comando); i++; t=i; } i=0; while(i<N_CASE && i!=t){ printf("\t ********DESCRIZIONE CASA %d: ***********\n\n",i+1); printf("Costo Casa: %d\n",casa[i].costo); printf("Superficie in metri quadri: %f\n",casa[i].metri_quadri); printf("Indirizzo: %f\n",casa[i].indirizzo); printf("Nome citta: %f\n",casa[i].nome_citta); printf("Tipologia:%d\n",casa[i].tipologia); i++; } system("PAUSE"); return 0; }
appena inserito il valore 9 il programma mi rimanda alla fine del primo ciclo while() come se vi fosse un errore

Per prima cosa trovo tutto questo uso dei typedef un po' eccessivo. Se scoprissi i template del c++ ne andresti pazzo.
gets non va mai usato, usa fgets(casa.indirizzo, N, stdin);
In ogni caso l'errore era legato al fatto che indirizzo era un array di array di char. Quindi levi stringa e metti char ...[N] che è meglio.
detto questo nelle stampe finali dovresti mettere %s e non %f per le stringhe.
gets non va mai usato, usa fgets(casa.indirizzo, N, stdin);
In ogni caso l'errore era legato al fatto che indirizzo era un array di array di char. Quindi levi stringa e metti char ...[N] che è meglio.
detto questo nelle stampe finali dovresti mettere %s e non %f per le stringhe.
Ti ringrazio per la risposta, ho fatto come hai detto però non mi lascia lo stesso scrivere l'indirizzo con il numero. Sembra voglia accettare solo caratteri!
A dir il vero la funzione fgets() non l'ho mai vista per cui non so a cosa serva....andrò a studiarmela
Ecco il codice: (Ho lasciato i typedef perchè lo richiede esplicitimente l'esercizio.)
A dir il vero la funzione fgets() non l'ho mai vista per cui non so a cosa serva....andrò a studiarmela
Ecco il codice: (Ho lasciato i typedef perchè lo richiede esplicitimente l'esercizio.)
#include <stdio.h> #include <stdlib.h> #define N_CASE 20 #define N 50 int main(int argc, char *argv[]) { typedef char stringa[N]; typedef enum{acquisto,vendita}ttipologia; typedef struct{ int costo; float metri_quadri; stringa indirizzo; stringa nome_citta; ttipologia tipologia; }tcasa; typedef tcasa elenco[N_CASE]; elenco casa; int i=0,t; int comando=1; while(i<N_CASE && comando==1){ printf("\t************ TALLA IMMOBILIARE ***********\n\n"); printf("\t CASA nr.%d\n\n",i+1); printf("Inserire il costo della casa:\n"); scanf("%d",&casa[i].costo); printf("Inserire la superficie della casa:\n"); scanf("%f",&casa[i].metri_quadri); printf("Inserire l'indirizzo:\n"); fgets(casa[i].indirizzo,N,stdin); printf("Inserire il nome della citta:\n"); scanf("%s",casa[i].nome_citta); printf("Inserire la tipologia della casa:\n"); scanf("%d",&casa[i].tipologia); while(casa[i].tipologia!=0 && casa[i].tipologia!=1){ printf("Il valore deve essere 0=acquisto,oppure 1=vendita.\nImmetti un nuovo valore per la tipologia:\n"); scanf("%d",&casa[i].tipologia); } printf("Terminare l'inserimento? SI=0 NO=1:\n"); scanf("%d",&comando); i++; t=i; } i=0; while(i<N_CASE && i!=t){ printf("\t ******** DESCRIZIONE CASA %d: ***********\n\n",i+1); printf("Costo Casa: %d\n",casa[i].costo); printf("Superficie in metri quadri: %f\n",casa[i].metri_quadri); printf("Indirizzo: %s\n",casa[i].indirizzo); printf("Nome citta: %f\n",casa[i].nome_citta); printf("Tipologia:%d\n",casa[i].tipologia); i++; } system("PAUSE"); return 0; }
In generale dicono di non usare mai la gets() perché è "scema" e legge senza tener conto della lunghezza della stringa; come ti hanno già suggerito quella più comoda è la fgets, oppure si può costruire una funzione leggi stringa di questo tipo:
int leggi_stringa ( char stringa[], int *p_lung_stringa) { int indice,carat; indice=0; while(((carat = getchar()) !='\n') && (carat!=EOF)) if ( indice < MAX_STRINGA) /* MAX_STRINGA definita con dei define*/ { stringa[indice] = (char) carat; indice++; } stringa[indice] = '\0'; *p_lung_stringa = indice; return(carat); /* restituisce EOF oppure \n */ }
Grazie Obidream, ma il tuo metodo mi sembra un tantino laborioso....In conclusione mi sorge una domanda spontanea...
non è possibile leggere da tastiera una stringa che contenga numeri oltre a caratteri?
non è possibile leggere da tastiera una stringa che contenga numeri oltre a caratteri?
fgets e gets non hanno alcun problema con la lettura di numeri. Il problema è probabilmente da cercare altrove.
Esatto, ma dove?
Io ho pensato che gets() non assegni automaticamente il testo alla variabile, per cui ho provato con il solito gets("%s",..)
ma evidentemente mi sbaglio anche così.
Forse c'è da aggiungere qualche libreria particolare all'inizio?
Vi ringrazio
Io ho pensato che gets() non assegni automaticamente il testo alla variabile, per cui ho provato con il solito gets("%s",..)
ma evidentemente mi sbaglio anche così.
Forse c'è da aggiungere qualche libreria particolare all'inizio?
Vi ringrazio
Ragazzi ho trovato qual era il problema!
Utilizzo la funzione gets() ma prima uso fflush(stdin); non ho ancora ben capito cosa serva ma mi elimina così l'errore!
Utilizzo la funzione gets() ma prima uso fflush(stdin); non ho ancora ben capito cosa serva ma mi elimina così l'errore!
scanf() lascia nel buffer di input (stdin) il carattere "a capo" \n. Quindi quando chiami fgets() nel buffer di input c'è una linea vuota; ma è pur sempre una linea, e quindi legge una linea vuota. Per svuotare il buffer di input usa
prima di chiamare gets() o fgets().
L'istruzione
ha comportamento indefinito. È sbagliata, sempre e comunque, il fatto che sembri funzionare è assolutamente un caso. La funzione fflush() deve essere usata solo con stream di output.
Maggiori informazioni qui.
while((c = getchar()) != '\n' && c != EOF) /* discard the character */;
prima di chiamare gets() o fgets().
L'istruzione
fflush(stdin)
ha comportamento indefinito. È sbagliata, sempre e comunque, il fatto che sembri funzionare è assolutamente un caso. La funzione fflush() deve essere usata solo con stream di output.
Maggiori informazioni qui.