I have a solution for the Kattis Problem https://open.kattis.com/problems/almostperfect. The solution is accepted, but the runtime is too long (>1.00s).
I tried everything to solve this issue. What can I do to further improve the performance of my code?
import java.io.FileInputStream;
import java.util.Scanner;
import java.io.*;
import java.util.*;
public class almostperfect {
public static int perfect(int number){
// 2 = perfect
// 1 = almost perfect
// 0 = not perfect
int sum = 0;
int b = 0;
for(int i=1;i<number;i++)
{
if(number%i==0)
{
sum = sum + i;
}
}
if(sum == number){
b = 2;
} else if(Math.abs(sum-number)<=2){
b = 1;
}
return b;
}
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
ArrayList<Integer> input = new ArrayList<Integer>();
int a;
int status;
while(scan.hasNextLong()){
input.add((int) scan.nextLong());
}
for(int i=0; i<input.size(); i++){
a = input.get(i);
status = perfect(a);
if(status==2){
System.out.println(a+" perfect");
} else if (status==1){
System.out.println(a+" almost perfect");
} else {
System.out.println(a+" not perfect");
}
}
}}
When you calculate the divisors of number, you don't have to loop from 1 to number, but to the square root of number. Take 100 for example - if 2 is a dividor of 100, so is 100/2.
int sum = 1; //1 is always a divisor
int b = 0;
int sqr = (int)Math.sqrt(number);
for(int i=2;i< sqr;i++)
{
if(number%i==0)
{
sum = sum + i;
sum = sum + number/i;
}
}
//Check what happens for sqr - if it's a divisor, add it only once
if (sqr * sqr == number)
sum += sqr;
Your code is fine, what is not fine is the method of finding the factors for the number it implements. You need to be smarter than brute force checking every possible number smaller than number if it is a factor.
First, obviously 1 is always a factor, since any number divides by 1 without a remainder. Also, by definition the number itself is not a factor. This restricts factors to be found to the range (2 ... n-1).
Second, if you find a divisor, then the dividend is also a divisor:
dividend = number / divisor -> implies: dividend is also a divisor
This means divisors are always found in pairs (dividend is also a divisor, making the pair). The one exception that must be accounted for is that dividend may be the same as dividend (e.g. number = 9, divisor = 3 -> dividend = 3). This can be exploited, leading to:
Third, when starting testing from the smallest possible divisor (2), the first dividend you find is the largest divisor possible, with dividends decreasing steadily while you increase the tested divisor. This means there is no need to explicitly check for divisors that are found as dividend. That means the upper testing limit would be where divisor and dividend become equal, in other words the root of number.
As stated for the problem in the link, numbers may be in range 1 ... 1E9. Your brute force method needs 1 billion tests for 1E9, while the smart version exploiting above properties, only needs 31621. Thats about factor 30000 faster!
Related
I am trying to write a Java method that checks whether a number is a perfect number or not.
A perfect number is a number that is equal to the sum of all its divisor (excluding itself).
For example, 6 is a perfect number because 1+2+3=6. Then, I have to write a Java program to use the method to display the first 5 perfect numbers.
I have no problem with this EXCEPT that it is taking forever to get the 5th perfect number which is 33550336.
I am aware that this is because of the for loop in my isPerfectNumber() method. However, I am very new to coding and I do not know how to come up with a better code.
public class Labreport2q1 {
public static void main(String[] args) {
//Display the 5 first perfect numbers
int counter = 0,
i = 0;
while (counter != 5) {
i++;
isPerfectNumber(i);
if (isPerfectNumber(i)) {
counter++;
System.out.println(i + " ");
}
}
}
public static boolean isPerfectNumber(int a) {
int divisor = 0;
int sum = 0;
for (int i = 1; i < a; i++) {
if (a % i == 0) {
divisor = i;
sum += divisor;
}
}
return sum == a;
}
}
This is the output that is missing the 5th perfect number
Let's check the properties of a perfect number. This Math Overflow question tells us two very interesting things:
A perfect number is never a perfect square.
A perfect number is of the form (2k-1)×(2k-1).
The 2nd point is very interesting because it reduces our search field to barely nothing. An int in Java is 32 bits. And here we see a direct correlation between powers and bit positions. Thanks to this, instead of making millions and millions of calls to isPerfectNumber, we will be making less than 32 to find the 5th perfect number.
So we can already change the search field, that's your main loop.
int count = 0;
for (int k = 1; count < 5; k++) {
// Compute candidates based on the formula.
int candidate = (1L << (k - 1)) * ((1L << k) - 1);
// Only test candidates, not all the numbers.
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
This here is our big win. No other optimization will beat this: why test for 33 million numbers, when you can test less than 100?
But even though we have a tremendous improvement, your application as a whole can still be improved, namely your method isPerfectNumber(int).
Currently, you are still testing way too many numbers. A perfect number is the sum of all proper divisors. So if d divides n, n/d also divides n. And you can add both divisors at once. But the beauty is that you can stop at sqrt(n), because sqrt(n)*sqrt(n) = n, mathematically speaking. So instead of testing n divisors, you will only test sqrt(n) divisors.
Also, this means that you have to start thinking about corner cases. The corner cases are 1 and sqrt(n):
1 is a corner case because you if you divide n by 1, you get n but you don't add n to check if n is a perfect number. You only add 1. So we'll probably start our sum with 1 just to avoid too many ifs.
sqrt(n) is a corner case because we'd have to check whether sqrt(n) is an integer or not and it's tedious. BUT the Math Overflow question I referenced says that no perfect number is a perfect square, so that eases our loop condition.
Then, if at some point sum becomes greater than n, we can stop. The sum of proper divisors being greater than n indicates that n is abundant, and therefore not perfect. It's a small improvement, but a lot of candidates are actually abundant. So you'll probably save a few cycles if you keep it.
Finally, we have to take care of a slight issue: the number 1 as candidate. 1 is the first candidate, and will pass all our tests, so we have to make a special case for it. We'll add that test at the start of the method.
We can now write the method as follow:
static boolean isPerfectNumber(int n) {
// 1 would pass the rest because it has everything of a perfect number
// except that its only divisor is itself, and we need at least 2 divisors.
if (n < 2) return false;
// divisor 1 is such a corner case that it's very easy to handle:
// just start the sum with it already.
int sum = 1;
// We can stop the divisors at sqrt(n), but this is floored.
int sqrt = (int)Math.sqrt(n);
// A perfect number is never a square.
// It's useful to make this test here if we take the function
// without the context of the sparse candidates, because we
// might get some weird results if this method is simply
// copy-pasted and tested on all numbers.
// This condition can be removed in the final program because we
// know that no numbers of the form indicated above is a square.
if (sqrt * sqrt == n) {
return false;
}
// Since sqrt is floored, some values can still be interesting.
// For instance if you take n = 6, floor(sqrt(n)) = 2, and
// 2 is a proper divisor of 6, so we must keep it, we do it by
// using the <= operator.
// Also, sqrt * sqrt != n, so we can safely loop to sqrt
for (int div = 2; div <= sqrt; div++) {
if (n % div == 0) {
// Add both the divisor and n / divisor.
sum += div + n / div;
// Early fail if the number is abundant.
if (sum > n) return false;
}
}
return n == sum;
}
These are such optimizations that you can even find the 7th perfect number under a second, on the condition that you adapt the code for longs instead of ints. And you could still find the 8th within 30 seconds.
So here's that program (test it online). I removed the comments as the explanations are here above.
public class Main {
public static void main(String[] args) {
int count = 0;
for (int k = 1; count < 8; k++) {
long candidate = (1L << (k - 1)) * ((1L << k) - 1);
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
}
static boolean isPerfectNumber(long n) {
if (n < 2) return false;
long sum = 1;
long sqrt = (long)Math.sqrt(n);
for (long div = 2; div <= sqrt; div++) {
if (n % div == 0) {
sum += div + n / div;
if (sum > n) return false;
}
}
return n == sum;
}
}
The result of the above program is the list of the first 8 perfect numbers:
6
28
496
8128
33550336
8589869056
137438691328
2305843008139952128
You can find further optimization, notably in the search if you check whether 2k-1 is prime or not as Eran says in their answer, but given that we have less than 100 candidates for longs, I don't find it useful to potentially gain a few milliseconds because computing primes can also be expensive in this program. If you want to check for bigger perfect primes, it makes sense, but here? No: it adds complexity and I tried to keep these optimization rather simple and straight to the point.
There are some heuristics to break early from the loops, but finding the 5th perfect number still took me several minutes (I tried similar heuristics to those suggested in the other answers).
However, you can rely on Euler's proof that all even perfect numbers (and it is still unknown if there are any odd perfect numbers) are of the form:
2i-1(2i-1)
where both i and 2i-1 must be prime.
Therefore, you can write the following loop to find the first 5 perfect numbers very quickly:
int counter = 0,
i = 0;
while (counter != 5) {
i++;
if (isPrime (i)) {
if (isPrime ((int) (Math.pow (2, i) - 1))) {
System.out.println ((int) (Math.pow (2, i -1) * (Math.pow (2, i) - 1)));
counter++;
}
}
}
Output:
6
28
496
8128
33550336
You can read more about it here.
If you switch from int to long, you can use this loop to find the first 7 perfect numbers very quickly:
6
28
496
8128
33550336
8589869056
137438691328
The isPrime method I'm using is:
public static boolean isPrime (int a)
{
if (a == 1)
return false;
else if (a < 3)
return true;
else {
for (int i = 2; i * i <= a; i++) {
if (a % i == 0)
return false;
}
}
return true;
}
Background:
I'm working on Project Euler problem #2 and I have a class built to solve the problem. For those who haven't done this before, this is the problem:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... .
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
The problem:
I have the program built to solve the problem given, but when I run it, I get the value -1833689714. This should not be the value returned, as I sum only positive numbers and no multiplications are performed that I know of. How do I fix this?
My code
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
int answer = resultsSum(fibonacci(4000000));
System.out.println(answer);
}
public static int resultsSum(ArrayList<Integer> resultList){
int total = 0;
for(Integer r : resultList){
total += r.intValue();
}
return total;
}
public static ArrayList<Integer> fibonacci(int n){
ArrayList fibEvens = new ArrayList<Integer>();
int a = 1;
int b = 2;
fibEvens.add(b);
for(int i = 1; i < (n - 1); i++) {
int tempVar = a;
a = b;
b += tempVar;
if(b % 2 == 0){
fibEvens.add(b);
}
}
return fibEvens;
}
}
https://projecteuler.net/problem=2
The reason you're getting a negative result is that the the terms of the Fibonacci sequence above the 88th term are in excess of the maximum positive value of the Long data type in java, which is 9,223,372,036,854,775,80.
When you attempt to increment the long data type beyond it's max value by one it wraps around to the minimum value (-9,223,372,036,854,775,80). At the point the Fibonacci terms exceed the max value, you're doing this many times over.
Additionally, the problem you've posted indicates that you should be stopping when the value of the number derived from the sequence is greater than 4 million, not trying to add the even values of the first 4 million values (which is huge).
It looks like your code is attempting to get to the 4-millionth Fibonacci number, which is quite different than the terms in the Fibonacci sequence whose values do not exceed four million.
Because
int data type stores number from -2^31 to 2^31 - 1
Integer data type stores number from -2^63 to 2^63 - 1
Try with BigInteger
public static BigInteger fibonacci(int n) {
BigInteger[] f = new BigInteger[n + 1];
f[0] = BigInteger.ZERO;
if (n > 0) {
f[1] = BigInteger.ONE;
}
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 1].add(f[i - 2]);
}
return f[n];
}
But the 4 million Fibonacci value very long. See more 300th, 500th
i want to find number of factors of a number say 900 that are less than its square root.
eg: there are 27 factors of 900 and i want to find number of factors smaller than root of 900 i.e, 30 which are 1,2,3,4,5,6,9,10,12,15,18,20,25.
i currently have this program that finds the number of factors by calculating the number of prime factors. eg:prime factors of 140 are:2^2*5*7. So the number of factors are:(2+1)(1+1)(1+1) [multiplication of powers of prime factors]
import java.io.*;
import java.util.*;
class Solution
{
// Program to print all prime factors
static void primeFactors(int n)
{
TreeMap tm=new TreeMap();
int times=0;
// Print the number of 2s that divide n
while (n%2 == 0)
{
System.out.println("2");
if(!tm.containsKey(2))
{
tm.put(2,1);
}
else
{
times=(int)tm.get(2);
tm.put(2,times+1);
}
n = n/2;
}
// n must be odd at this point. So we can skip one element (Note i = i +2)
for (int i = 3; i <= Math.sqrt(n); i = i+2)
{
// While i divides n, print i and divide n
while (n%i == 0)
{
System.out.println(i);
if(!tm.containsKey(i))
{
tm.put(i,1);
}
else
{
times=(int)tm.get(i);
tm.put(i,times+1);
}
n = n/i;
}
}
// This condition is to handle the case whien n is a prime number
// greater than 2
if (n > 2)
{
System.out.println(n);
if(!tm.containsKey(n))
{
tm.put(n,1);
}
else
{
times=(int)tm.get(n);
tm.put(n,times+1);
}
}
/////////////////////////////////////////////////////////////////////////////
Set set = tm.entrySet();
System.out.println(tm);
Iterator num = set.iterator();
int key=0;
int sum=1;
while (num.hasNext())
{
Map.Entry number =(Map.Entry)num.next();
sum=sum*((int)number.getValue()+1);
}
System.out.println(sum);
}
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
primeFactors(n);
}
}
here i am getting number of factors eg:27 factors for 900 but i want to find number of factors that are less than 30. Thanks for help.
If you have the number of factors of n, simply integer divide by 2 to get the number of factors less than the square root. This works because each factor d of n less than sqrt(n) corresponds to a factor greater than sqrt(n) (namely n/d), so the number of such factors will be half the total (unless n is a perfect square, in which case sqrt(n) is an extra factor). However, integer division by 2 takes care of that corner case. Indeed, 27/2 = 13 as desired.
It was asked to find a way to check whether a number is in the Fibonacci Sequence or not.
The constraints are
1≤T≤10^5
1≤N≤10^10
where the T is the number of test cases,
and N is the given number, the Fibonacci candidate to be tested.
I wrote it the following using the fact a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square :-
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i = 0 ; i < n; i++){
int cand = sc.nextInt();
if(cand < 0){System.out.println("IsNotFibo"); return; }
int aTest =(5 * (cand *cand)) + 4;
int bTest = (5 * (cand *cand)) - 4;
int sqrt1 = (int)Math.sqrt(aTest);// Taking square root of aTest, taking into account only the integer part.
int sqrt2 = (int)Math.sqrt(bTest);// Taking square root of bTest, taking into account only the integer part.
if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
}
But its not clearing all the test cases? What bug fixes I can do ?
A much simpler solution is based on the fact that there are only 49 Fibonacci numbers below 10^10.
Precompute them and store them in an array or hash table for existency checks.
The runtime complexity will be O(log N + T):
Set<Long> nums = new HashSet<>();
long a = 1, b = 2;
while (a <= 10000000000L) {
nums.add(a);
long c = a + b;
a = b;
b = c;
}
// then for each query, use nums.contains() to check for Fibonacci-ness
If you want to go down the perfect square route, you might want to use arbitrary-precision arithmetics:
// find ceil(sqrt(n)) in O(log n) steps
BigInteger ceilSqrt(BigInteger n) {
// use binary search to find smallest x with x^2 >= n
BigInteger lo = BigInteger.valueOf(1),
hi = BigInteger.valueOf(n);
while (lo.compareTo(hi) < 0) {
BigInteger mid = lo.add(hi).divide(2);
if (mid.multiply(mid).compareTo(x) >= 0)
hi = mid;
else
lo = mid.add(BigInteger.ONE);
}
return lo;
}
// checks if n is a perfect square
boolean isPerfectSquare(BigInteger n) {
BigInteger x = ceilSqrt(n);
return x.multiply(x).equals(n);
}
Your tests for perfect squares involve floating point calculations. That is liable to give you incorrect answers because floating point calculations typically give you inaccurate results. (Floating point is at best an approximate to Real numbers.)
In this case sqrt(n*n) might give you n - epsilon for some small epsilon and (int) sqrt(n*n) would then be n - 1 instead of the expected n.
Restructure your code so that the tests are performed using integer arithmetic. But note that N < 1010 means that N2 < 1020. That is bigger than a long ... so you will need to use ...
UPDATE
There is more to it than this. First, Math.sqrt(double) is guaranteed to give you a double result that is rounded to the closest double value to the true square root. So you might think we are in the clear (as it were).
But the problem is that N multiplied by N has up to 20 significant digits ... which is more than can be represented when you widen the number to a double in order to make the sqrt call. (A double has 15.95 decimal digits of precision, according to Wikipedia.)
On top of that, the code as written does this:
int cand = sc.nextInt();
int aTest = (5 * (cand * cand)) + 4;
For large values of cand, that is liable to overflow. And it will even overflow if you use long instead of int ... given that the cand values may be up to 10^10. (A long can represent numbers up to +9,223,372,036,854,775,807 ... which is less than 1020.) And then we have to multiply N2 by 5.
In summary, while the code should work for small candidates, for really large ones it could either break when you attempt to read the candidate (as an int) or it could give the wrong answer due to integer overflow (as a long).
Fixing this requires a significant rethink. (Or deeper analysis than I have done to show that the computational hazards don't result in an incorrect answer for any large N in the range of possible inputs.)
According to this link a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square so you can basically do this check.
Hope this helps :)
Use binary search and the Fibonacci Q-matrix for a O((log n)^2) solution per test case if you use exponentiation by squaring.
Your solution does not work because it involves rounding floating point square roots of large numbers (potentially large enough not to even fit in a long), which sometimes will not be exact.
The binary search will work like this: find Q^m: if the m-th Fibonacci number is larger than yours, set right = m, if it is equal return true, else set left = m + 1.
As it was correctly said, sqrt could be rounded down. So:
Even if you use long instead of int, it has 18 digits.
even if you use Math.round(), not simply (int) or (long). Notice, your function wouldn't work correctly even on small numbers because of that.
double have 14 digits, long has 18, so you can't work with squares, you need 20 digits.
BigInteger and BigDecimal have no sqrt() function.
So, you have three ways:
write your own sqrt for BigInteger.
check all numbers around the found unprecise double sqrt() for being a real sqrt. That means also working with numbers and their errors simultaneously. (it's horror!)
count all Fibonacci numbers under 10^10 and compare against them.
The last variant is by far the simplest one.
Looks like to me the for-loop doesn't make any sense ?
When you remove the for-loop for me the program works as advertised:
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int cand = sc.nextInt();
if(cand < 0){System.out.println("IsNotFibo"); return; }
int aTest = 5 * cand *cand + 4;
int bTest = 5 * cand *cand - 4;
int sqrt1 = (int)Math.sqrt(aTest);
int sqrt2 = (int)Math.sqrt(bTest);
if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
You only need to test for a given candidate, yes? What is the for loop accomplishing? Could the results of the loop be throwing your testing program off?
Also, there is a missing } in the code. It will not run as posted without adding another } at the end, after which it runs fine for the following input:
10 1 2 3 4 5 6 7 8 9 10
IsFibo
IsFibo
IsFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo
Taking into account all the above suggestions I wrote the following which passed all test cases
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long[] fib = new long[52];
Set<Long> fibSet = new HashSet<>(52);
fib[0] = 0L;
fib[1] = 1L;
for(int i = 2; i < 52; i++){
fib[i] = fib[i-1] + fib[i - 2];
fibSet.add(fib[i]);
}
int n = sc.nextInt();
long cand;
for(int i = 0; i < n; i++){
cand = sc.nextLong();
if(cand < 0){System.out.println("IsNotFibo");continue;}
if(fibSet.contains(cand)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
}
I wanted to be on the safer side hence I choose 52 as the number of elements in the Fibonacci sequence under consideration.
While trying to devise an algorithm, I stumbled upon this question. It's not homework.
Let P_i = an array of the first i primes. Now I need the smallest i such that
Sum<n=0..i> 1 / (P_i[n]*P_i[n]) >= 1.
(if such i exists).
An approximation for the i'th prime is i*log(i). So I tried this in Java:
public static viod main(String args[]) {
double sum = 0.0;
long i = 2;
while(sum<1.0) {
sum += 1.0 / (i*Math.log(i)*i*Math.log(i));
i++;
}
System.out.println(i+": "+sum);
}
However the above doesn't finish because it converges to 0.7. However 1/100000000^2 rounds to 0.0 in Java, so that's why it doesn't work. For the same reason it doesn't even work if you replace the 6th line with
sum += 1.0 / (i*i)
while that should reach 1 if I'm not mistaken, because the sum should incease faster than 1/2^i and the latter converges to 1. In other words, this shows that Java rounding causes the sum to not reach 1. I think that the minimum i of my problem should exist.
On the maths side of this question, not the java side:
If I understand the problem, there is no solution (no value of i).
For any finite set P_i of primes {p_1, p_2,...p_i} let N_i be the set of all integers up to p_i, {1,2,3,...,p_i}. The sum 1/p^2 (for all p_n in P_i) will be less than the sum of all 1/x^2 for x in N_i.
The sum of 1/x^2 tends to ~1.65 but since 1 will never be in the set of primes, the sum is limited by ~0.65
You cannot use double for this, because it is not uniform. You should use fractions. I found this class https://github.com/kiprobinson/BigFraction
Then I tried to find whats happening :
public static void main(String args[]) {
BigFraction fraction = BigFraction.valueOf(1, 4);
int n = 10000000, status = 1, num = 3;
double limit = 0.4;
for (int count = 2; count <= n;) {
for (int j = 2; j <= Math.sqrt(num); j++) {
if (num % j == 0) {
status = 0;
break;
}
}
if (status != 0) {
fraction = fraction.add(BigFraction.valueOf(1,BigInteger.valueOf(num).multiply(BigInteger.valueOf(num))));
if (fraction.doubleValue() >= limit){
System.out.println("reached " + limit + " with " + count + " firsts prime numbers");
limit += 0.01;
}
count++;
}
status = 1;
num++;
}
}
This is having this output :
reached 0.4 with 3 firsts prime numbers
reached 0.41000000000000003 with 4 firsts prime numbers
reached 0.42000000000000004 with 5 firsts prime numbers
reached 0.43000000000000005 with 6 firsts prime numbers
reached 0.44000000000000006 with 8 firsts prime numbers
reached 0.45000000000000007 with 22 firsts prime numbers
And nothing more in a minute. I debug it and found that it grows extremely slower and slower, I do not think, it can reach 1 even in infinity :) (but dont know how to prove it).
I guess you might loose the precision you need when you use default Math.log multiplied by float i. I think this can be handled by using an appropriate RoundingMode. Please see setRoundingMode