Somma dei divisori di un numero

di il
5 risposte

Somma dei divisori di un numero

Salve a tutti,
Sto codificando un programma che deve essere il più efficiente possibile e all'interno di questo programma devo trovare la somma dei divisori propri di un numero n (con n che va da un MAX, 1 milione, a 1). Ho trovato due metodi per fare ciò, il primo è questo:

int main(){
	int n=MAX,i,sum;
	while(n--){
	for(i=2,sum=1;i<=sqrt(n);i++)
		if(n%i == 0)
			(i!=n/i)? sum+=i+(n/i) : sum+=i;
	}
	return 0;
}
e il secondo è questo:

int main(){
	int n=MAX,app,div,esp,sum;
	while(n--){
		sum=1;
		app=n;
		div=2;
		while (div<=app) {
			esp=0;
        	if (app%div == 0){
            	while (app%div == 0){
                	esp++;
            		app /= div;
				}
			sum*=((pow(div,esp+1)-1)/(div-1));
       	 	}
       	 	else
       	    	div++;
		}
		sum-=n;
	}
	return 0;
}
Al momento sto lavorando sulla velocità di questi due programmi e non mi interessa usare la sum che calcolo (la userò poi nel programma completo in cui sfrutterò uno dei due algoritmi). Io credevo che con la fattorizzazione sarei stato molto più veloce e invece (al momento) non è così.
La domanda è, son io che non ho migliorato abbastanza l'algoritmo della fattorizzazione o è giusto e normale che sia più lento?

Ci tengo anche a precisare che programmo in C da circa due mesi, perciò qualsiasi consiglio (o 'trucco') per massimizzare l'efficienza di un programma (e per programmare meglio) è ovviamente ben accetto!

Questo è la prima discussione che apro su questo Forum, perciò spero di aver fatto tutto correttamente.

Grazie in anticipo, e buone feste!

5 Risposte

  • Re: Somma dei divisori di un numero

    Per velocizzare un algoritmo RARAMENTE ci sono trucchi legati al linguaggio di programmazione (ovviamente a meno che uno non sia proprio bischero ed abbia fatto un'implementazione sbagliata).

    Nel 90% dei casi serve un'implementazione piu' intelligente.

    Ad esempio: perche' non pre-calcolare tutti i primi compresi tra 1 e sqrt(1,000,000), in modo da EVITARE di fare dei test con degli interi che NON SONO primi ???

    Tabelle di primi gia' pronte c'e' ne sono a bizzeffe su Internet.
  • Re: Somma dei divisori di un numero

    La prima, fondamentale domanda è: per quale motivo stai cercando di calcolare in quel modo la sommatoria dei divisori propri di un naturale? Stai per caso cercando di generare in modo naif tutti i numeri perfetti in un range dato?
    In tal caso, esistono metodi notevolmente più efficienti, che coinvolgono anche i primi di Mersenne: salvo comunque il fatto che tali numeri sono già noti (alcuni numeri perfetti sono conosciuti da un paio di millenni) e basta semplicemente usare una look-up table, non occorre certo generarli dinamicamente.

    In ogni caso, l'ottimizzazione del codice è un'arte oscura che richiede un lunghissimo apprendistato e un paio di pile di libri ad altezza d'uomo, con competenze che partono dalla complessità computazionale e arrivano senza soluzione di continuità alla microottimizzazione in Assembly o con l'uso di builtins. A ciò si deve aggiungere una terza pila di libri, ma alta fino al secondo piano, se si parla di algoritmi sugli interi su piattaforme di risulta come x86, che pagano ancora nel terzo millennio un pegno mostruoso alla retrocompatibilità con operazioni come IMUL e IDIV che sprecano decine di cicli macchina, obbligando all'uso di tecniche raffinatissime per evitare ogni tipo di moltiplicazione, divisione e modulo (resto) negli inner loop.

    Anche per numeri piccoli, entrare nel ginepraio degli algoritmi di fattorizzazione (che per natura hanno una complessità computazionale proibitiva) richiede l'uso massiccio di accorgimenti che sarebbe proibitivo elencare in un thread sul forum.
  • Re: Somma dei divisori di un numero

    migliorabile ha scritto:


    Ad esempio: perche' non pre-calcolare tutti i primi compresi tra 1 e sqrt(1,000,000), in modo da EVITARE di fare dei test con degli interi che NON SONO primi ???

    Tabelle di primi gia' pronte c'e' ne sono a bizzeffe su Internet.
    Scusa la mia ignoranza ma credo di non aver capito perfettamente cosa intendi
    Dici di implementare un piccolo ciclo che mi faccia usare direttamente solo numeri primi come "div" nella fattorizzazione?

    M.A.W. 1968 ha scritto:


    La prima, fondamentale domanda è: per quale motivo stai cercando di calcolare in quel modo la sommatoria dei divisori propri di un naturale? Stai per caso cercando di generare in modo naif tutti i numeri perfetti in un range dato?
    In tal caso, esistono metodi notevolmente più efficienti, che coinvolgono anche i primi di Mersenne: salvo comunque il fatto che tali numeri sono già noti (alcuni numeri perfetti sono conosciuti da un paio di millenni) e basta semplicemente usare una look-up table, non occorre certo generarli dinamicamente.
    Il programma che sto cercando di fare deve trovare tutte le coppie di numeri amicabili (es: 220 e 284) in un range dato (io ho detto un milione, ma si può andare anche fino a un miliardo) nel minor tempo possibile.
    Quindi sto cercando di velocizzare al massimo la somma dei divisori.

    A questo punto però credo che devo concentrarmi di più sull'evitare di far fare calcoli inutili al programma
  • Re: Somma dei divisori di un numero

    Vale quanto già scritto a proposito dei numeri perfetti, strettamente correlati anche con gli amicabili. Leggi anche questo. Se l'esigenza è la velocità, nulla può battere l'uso di una lookup table precalcolata e quindi la generazione realtime è del tutto da escludere, per quanto si possano inventare cervellotiche ottimizzazioni - a meno che non si parli di un mero esercizio, stile Project Euler o simili.
  • Re: Somma dei divisori di un numero

    M.A.W. 1968 ha scritto:


    Vale quanto già scritto a proposito dei numeri perfetti, strettamente correlati anche con gli amicabili. Leggi anche questo. Se l'esigenza è la velocità, nulla può battere l'uso di una lookup table precalcolata e quindi la generazione realtime è del tutto da escludere, per quanto si possano inventare cervellotiche ottimizzazioni - a meno che non si parli di un mero esercizio, stile Project Euler o simili.
    Grazie mille ora guardo bene il link che mi hai fornito
    Comunque la generazione deve essere per forza a run time, per esercizio ovviamente, e il tempo da raggiungere dovrebbe essere circa 2 secondi (con 5 milioni come MAX).
Devi accedere o registrarti per scrivere nel forum
5 risposte