Given a natural number \$n\$ (\$1 \le n \le 500000\$), please output the summation of all its proper divisors.
Definition: A proper divisor of a natural number is the divisor that is strictly less than the number.
e.g. number 20 has 5 proper divisors: 1, 2, 4, 5, 10, and the divisor summation is: 1 + 2 + 4 + 5 + 10 = 22.
Input
An integer stating the number of test cases (equal to about 200000), and that many lines follow, each containing one integer between 1 and 500000 inclusive.
Output
One integer each line: the divisor summation of the integer given respectively.
My solution:
It solved the problem in 1.63 seconds, which seems suboptimal considering its 3-second limit.
import java.util.ArrayList;
import java.util.Scanner;
public class Main
{
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int testcases = reader.nextInt();
reader.nextLine();
ArrayList<Integer> values = new ArrayList<Integer>();
for(int i = 0; i<testcases; i++){
values.add(reader.nextInt());
reader.nextLine();
}
reader.close();
for(int i = 0; i<testcases; i++){
int sum = 1;
int testcase = values.get(i);
double lim = Math.sqrt(testcase);
for(int k = 2; k<=lim; k++){
if(testcase%k==0){
sum+=k;
int z = testcase/k;
if(k != z)
sum+=z;
}
}
if(testcase==1)
sum=0;
if(i<testcases-1)
System.out.println(sum);
else
System.out.print(sum);
}
}
}
EDIT: Updated code, which ran in a more respectable 0.22 seconds.
Taking out reader.nextLine();
improved the runtime by around 0.2 seconds.
Adding a special treatment to perfect squares seemed to have no noticable impact on speed (might depend on test cases?).
It turned out using BufferedReader
and BufferedWriter
had an immense impact on IO speed, reducing runtime to 0.22 seconds.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Main
{
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
int testcases = Integer.parseInt(in.readLine());
int[] values = new int[testcases];
for(int i = 0; i<testcases; i++){
values[i]=Integer.parseInt(in.readLine());
}
for(int i = 0; i<testcases; i++){
int sum = 1;
int testcase = values[i];
double lim = Math.sqrt(testcase);
for(int k = 2; k<lim; k++){
if(testcase % k == 0){
sum += k + testcase / k;
}
}
int intLim = (int) lim;
if (intLim * intLim == testcase) {
sum += lim;
}
if(testcase == 1)
sum=0;
out.write(sum + "\n");
}
out.flush();
}
}
System.out.write
but that doesn't speed things up. The only way that I know is to output to a file instead of stdout, as this bypasses the OS rendering terminals and such. Btw, your algorithm will miss some output: for examplesqrt(20)
is about 4.47 so you will miss divisors 5 and 10. \$\endgroup\$Scanner
is a beast, performance-wise. Use aBufferedReader
andInteger.parseInt()
. --- Also, you are wasting time building anArrayList<Integer>
, boxing all the values and building up the list. Just perform your logic as you read the values. --- You should move the inside of thefor
loop to a method that returns the sum, and print in the caller. This will allow adding unit tests to your code, and isolates the logic from the I/O, for a better structured program. \$\endgroup\$BufferedReader
andBufferedWriter
indeed had quite a big impact on IO speed. The code ran in 0.22 seconds. I also used an integer array instead of anArrayList
. \$\endgroup\$