No problema Divisor Summation , você deve calcular a somatório de todos os os divisores próprios (um divisor estritamente menor que n) de um número n (1 <= n <= 500000). Por exemplo, o somatório dos divisores próprios de 20 é 1 + 2 + 4 + 5 + 10 = 22.
A seguir, o algoritmo trivial para esse problema:
int soma(int n){
int i;
int sum=0;
for(i=1;i<n;i++){
if(n%i==0){
sum +=i;
}
}
return sum;
}
Infelizmente ou felizmente, o algoritmo acima excede o tempo limite estabelecido para o problema. Precisamos torná-lo mais rápido. Primeiramente, vamos mostrar que o seguinte resultado é válido:
Teorema 1: Seja d o menor divisor de um número composto n então d ≤
.
Suponha por absurdo que d >
. Logo,
d2 > n
d > n/d
Se d é um divisor de n então existe um k tal que n=k*d, ou seja, k=n/d. Logo, n/d também é divisor de n. Absurdo, encontramos um divisor menor que d.
Com esse resultado, podemos dividir o conjunto dos números divisores de um número em três conjuntos
,
e
nos divisores menores que
, maiores que
e divisores iguais
, respectivamente. Para cada divisor no conjunto
podemos encontrar um divisor no conjunto
. O conjunto
é igual
se o número não é um quadrado perfeito ou é unitário se o número é um quadrado perfeito. Baseado nessa divisão, nós podemos desenvolver o seguinte algoritmo:
int soma(int n){
int i;
int sum = 1;
if(n==1 ) return 0;
for(i=2;i*i<n;i++){
if(n%i==0) {
sum+=i; sum+=n/i;
}
}
if(i*i==n) sum += i;
return sum;
}
Para evitar o excesso de multiplicação dentro for podemos retirar a multiplicação calculando o valor do limite antes do início do for da seguinte maneira.
int soma(int n){
int i;
int sum = 1;
double limite = sqrt(n);
if(n==1) return 0;
for(i=2;i<limite;i++){
if(n%i==0) {
sum+=i; sum+=n/i;
}
}
if(i*i==n)sum += i;
return sum;
}
Podemos desenvolver uma solução ainda mais eficiente. A idéia básica do algoritmo que vamos desenvolver é a seguinte: vamos pré-calcular a soma dos divisores para todos os números da entrada em uma tabela. Inicialmente, a tabela começa com todas as somas iguais a zerados. Para cada número i, acrescentamos i para todos múltiplos de i a partir do número 2i.
#include <stdio.h>
#include <string.h>
#define MAX 500001
int sum[MAX];
void preprocess(){
int i,j;
memset(sum,0,sizeof(sum)); //zerar o vetor sum
for(i=1;i<MAX;i++){
for(j=i+i;j<MAX;j=j+i){
sum[j] +=i;
}
}
}
int soma(int n){
return sum[n];
}
Nenhum comentário:
Postar um comentário