Calculate amicable numbers efficiently to a very high upper limit in java - java

I have a program that computes amicable pairs of numbers up to a certain limit and prints them out to Stdout. FYI - two numbers are amicable if each is equal to the sum of the proper divisors of the other (for example, 220 and 284).
My program works fine for the first 8 or so pairs, but the problem is when the limit gets to a very high number e.g 5 million it becomes very slow. Is there a way to optimize this so that it computes much faster?
Here is my code
public class Amicable{
private static int num1, num2, limit = 5000000;
public static void main(String[] args){
for(int i = 1; i < limit;i++){
num1= sumOfDivisors(i);
num2 = sumOfDivisors(i)
if(num1 == num2){
System.out.println(num2 + " " + num1);
}
}
}
}
private static int sumOfDivisors(int n){
int sum = 0;
for(int i=1;i<=n/2;i++){
if(n%i==0){
sum =sum+i;
}
}
return sum;
}
}

You should make the innermost loop efficient.
static int sumOfDivisors(int n) {
int sum = 1;
int max = (int) Math.sqrt(n);
if (n > 1 && max * max == n) // in case of n is perfect square number
sum -= max;
for (int i = 2; i <= max; ++i)
if (n % i == 0)
sum += i + n / i;
return sum;
}
If n = 220, it is divisible by 2, so you can add 2 and 110 to the sum at the same time. However, if n is a perfect square number, for example n = 100, it is divisible by 10, but if 10 and 10 are added, they will duplicate, so subtract it in advance.
output for limit =2000000:
220 284
1184 1210
2620 2924
5020 5564
6232 6368
10744 10856
12285 14595
17296 18416
63020 76084
66928 66992
.....
1468324 1749212
1511930 1598470
1669910 2062570
1798875 1870245
It took 7 seconds on my PC.

You could change the statement of "if(i<num1)" to "if(i<num1 || num1<limit)".

This is a more efficient and faster approach.
Inside sumOfDivisors:
Start iteration from 2. (no a big deal) and add 1 during return time with sum as 1 is also a divisor.
Modify loop termination logic, i <= Math.sqrt(n).
For any number ‘num’ all its divisors are always less than and equal to ‘num/2’ and all prime factors are always less than and equal to sqrt(num).
If n%i == 0, then check, both divisors are are equal or not. If equal, take one if not equal then take both one.
private static int sumOfDivisors(int n) {
int sum = 0;
int sqrt = Math.sqrt(n);
for (int i = 2; i <= sqrt; i++) {
if (n % i == 0) {
if(i == (n/i))
sum = sum + i;
else
sum += (i+ n/i);
}
}
return sum+1;
}

Related

Highly divisible triangular number

Highly divisible triangular number, my solution
My solution of challenge from Project Euler takes too much time to execute. Although on lower numbers it works fine. Anyone could look up to my code and give me any advice to improve it?
The content of the task is:
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
Let us list the factors of the first seven triangle numbers:
(1: 1),
(3: 1,3),
(6: 1,2,3,6),
(10: 1,2,5,10),
(15: 1,3,5,15),
(21: 1,3,7,21),
(28: 1,2,4,7,14,28).
We can see that 28 is the first triangle number to have over five divisors.
What is the value of the first triangle number to have over five hundred divisors?
`public static void main(String[] args) {
int sum = 0;
int count = 0;
for (int i = 1; i <= Integer.MAX_VALUE; i++) {
sum += i;
for (int j = 1; j <= sum; j++) {
if (sum % j == 0) {
count++;
}
}
if (count > 500) {
System.out.println(sum);
break;
} else {
count = 0;
}
}
}`
Consider any set of primes (p1, p2, p3. . . pn) that factor a given number. Then the total number of divisors of that number are the products of (e1 + 1, e2 + 1, e3 + 1 ... en+1) where e is the exponent of the corresponding prime factor (i.e. the number of times that prime divides some number N).
There are three pieces to this answer.
The main driver code
the method to find the prime divisors.
and a Primes class that generates successive primes using an iterator.
The Driver
first instantiate the Primes class and initialize the triangleNo
Then continually generate the next triangular number until the returned divisor count meets the requirements.
Primes primes = new Primes();
long triangleNo = 0;
for (long i = 1;; i++) {
triangleNo += i;
int divisors = getDivisorCount(triangleNo, primes);
if (divisors > 500) {
System.out.println("No = " + triangleNo);
System.out.println("Divisors = " + divisors);
System.out.println("Nth Triangle = " + i);
break;
}
}
prints
No = 76576500
Divisors = 576
Nth Triangle = 12375 NOTE: this is the value n in n(n+1)/2.
Finding the prime factors
takes a value to factor and an instance of the Primes class
continues to test each prime for division, counting the number of times the remainder is 0.
a running product totalDivisors is computed.
the loop continues until t is reduced to 1 or the current prime exceeds the square root of the original value.
public static int getDivisorCount(long t, Primes primes) {
primes.reset();
Iterator<Integer> iter = primes;
int totalDivisors = 1;
long sqrtT = (long)Math.sqrt(t);
while (t > 1 && iter.hasNext()) {
int count = 0;
int prime = iter.next();
while (t % prime == 0) {
count++;
t /= prime;
}
totalDivisors *= (count + 1);
if (prime > sqrtT) {
break;
}
}
return totalDivisors;
}
The Primes class
A class to generate primes with a resettable iterator (to preserve computed primes from previous runs the index of an internal list is set to 0.)
an iterator was chosen to avoid generating primes which may not be required to obtain the desired result.
A value to limit the largest prime may be specified or it may run to the default.
Each value is tested by dividing by the previously computed primes.
And either added to the current list or ignored as appropriate.
if more primes are needed, hashNext invoked a generating to increase the number and returns true or false based on the limit.
class Primes implements Iterator<Integer> {
private int lastPrime = 5;
private List<Integer> primes =
new ArrayList<>(List.of(2, 3, 5));
private int index = 0;
private int max = Integer.MAX_VALUE;
public Primes() {
}
public Primes(int max) {
this.max = max;
}
#Override
public boolean hasNext() {
if (index >= primes.size()) {
generate(15); // add some more
}
return primes.get(index) < max;
}
#Override
public Integer next() {
return primes.get(index++);
}
private void generate(int n) {
outer: for (int candidate = lastPrime + 2;;
candidate += 2) {
for (int p : primes) {
if (p < Math.sqrt(candidate)) {
if (candidate % p == 0) {
continue outer;
}
}
}
primes.add(candidate);
lastPrime = candidate;
if (n-- == 0) {
return;
}
}
}
public void reset() {
index = 0;
}
}
Note: It is very likely that this answer can be improved, by employing numerical shortcuts using concepts in the area of number theory or perhaps even basic algebra.

Write a program that will calculate the number of trailing zeros in a factorial of a given number. N! = 1 * 2 * 3 * ... * N

This is my code for the Codewars problem (Java) yet I cannot make it work. I'm pretty sure I've made a stupid mistake somewhere because of my lack of experience (coding for 4 months)
public static int zeros(int n) {
int f = 1;
int zerocount = 0;
for(int i = 2 ; i <= n; i++){
f *= i;
}
String factorial = String.valueOf(f);
String split [] = factorial.split("");
for(int i = 0; i < split.length; i++){
String m = split[i];
if(m.equals( "0")){
zerocount ++;
}
else {
zerocount = 0;
}
}
return zerocount;
}
}
In fact, you do not need to calculate the factorial because it will rapidly explode into a huge number that will overflow even a long. What you want to do is count the number of fives and twos by which each number between 2 and n can be divided.
static int powersoffive(int n) {
int p=0;
while (n % 5 == 0) {
p++;
n /= 5;
}
return p;
}
static int countzeros(int n) {
int fives = 0;
for (int i = 1; i <= n; i++)
fives += powersoffive(i);
return fives;
}
Note: Lajos Arpad's solution is superior.
As pointed out by other users your solution will probably not be accepted because of the exploding factorial you are calculating.
About the code you wrote there are two mistakes you have made:
You are calculating the factorial in the wrong way. You should start with i = 2 in the loop
for(int i = 2; i <= n; i++){
f *= i;
}
Also in Java you cannot compare strings using ==. This is not valid
if(m == "0")
You should compare them like this
if(m.equals("0"))
Anyway this is how I would have resolved the problem
public static int zeros(int n) {
int zerocount = 0;
for (int i = 5; n / i > 0; i *= 5) {
zerocount += n / i;
}
return zerocount;
}
A zero in a base-10 representation of a number is a 2*5. In order to determine the number of trailing zeroes you will need to determine how many times can you divide your number with ten, or, in other words, the minimum of the sum of 2 and 5 factors. Due to the fact that 5 is bigger than 2 and we go sequentially, the number of fives will be the number of trailing zeroes.
A naive approach would be to round down n/5, but that will only give you the number of items divisible with 5. However, for example, 25 is divisible by 5 twice. The same can be said about 50. 125 can be divided by 5 three times, no less.
So, the algorithm would look like this:
int items = 0;
int power = 5;
while (power < n) {
items += (int) (n / power);
power *= 5;
}
Here small numbers are in use in relative terms, but it's only a proof of concept.
You do need to use brute force here and you integers will overflow anyway.
With multiplication trailing zero appears only as the result of 2*5.
Now imagine the factorial represented by a product of it's prime factors.
Notice that for every 5 (five) we will always have 2 (two).
So to calculate the number of zeroes we need to calculate the number of fives.
That can be implemented by continuously dividing N by five and totaling results
In Java code that will be something like this:
static int calculate(int n)
{
int result = 0;
while (n > 0 ) {
n /= 5;
result += n;
}
return result;
}

euler project 23 - can't find the mistake in my code [Java]

After a week that I spent stuck on this problem I can't find where is my mistake.
the problem is:
A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
so my code is:
package eulerProject;
import java.util.*;
import java.math.BigInteger;
public class e23 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
BigInteger sum = BigInteger.ZERO;
for (int i = 1; i <= 28123; i++) {
if (!check(i))
list.add(i);
}
System.out.println(list);
for (int i = 0; i < list.size(); i++)
sum = sum.add(BigInteger.valueOf(list.get(i)));
System.out.println(sum);
}
public static boolean check(long z) {
long y = 0;
for (long i = 1; i <= z / 2; i++) {
if (abundant(i)) {
y = z - i;
if (abundant(y)) {
return true;
}
y = 0;
}
}
return false;
}
public static long sum(long x) {
long sum = 0;
for (int i = 1; i < (Math.sqrt(x)); i++) {
if (x % i == 0) {
if (x / i == i) {
sum += i;
} else {
sum = sum + i + (x / i);
}
}
}
sum = sum - x;
return sum;
}
public static boolean abundant(long x) {
if (sum(x) > x)
return true;
return false;
}
}
I'll just explain the methods:
"sum" - sums all the proper divisors of a number.
(like number = 12 , so it sum: 1+2+3+4+6.)
"abundant" - just checks if the number is abundant or not by compairing the sum of his divisors and the number itself.
"check" - generating two numbers which their sum is the number we checking - and checking if the both numbers are abundant. if they are so returns true.
and the main just generating numbers until the max limit, adding to list and then I sum the list.
my answer is: 4190404.
the correct answer is: 4179871.
where is the mistake?
Your sum method doesn't get the correct sum for perfect squares because your loop stops before the square root. For example, if you called sum(16), the loop would run up to i = 3 and stop, so 4 would not contribute to the sum.
Solution:
(I also fixed some inefficiencies.)
public static long sum(long x){
long sum = 1;
int sqrt = (int)Math.sqrt(x);
for (int i = 2; i <= sqrt; i++) {
if (x % i == 0) {
sum += i + (x/i);
}
}
//checks if perfect square and subtracts out extra square root.
if(sqrt * sqrt == x) sum -= sqrt;
return sum;
}

In Java how to find random factors of a given number

I have a given number. How can I find the factors of that number (for example, 5 and 3 for the number 15)? Here is the code I tried:
int factor1 = 2;
while ((a % factor1 != 0) && (a >= factor1)) {
d++;
}
if (factor1 == a){
d = 1;
}
But this gives me only the smallest factor (i.e a=3 all the time). I would like to get a random set of factors.
Loop through each number from 1 to N inclusively using the modulus operator (%). If n%currentNumber==0, they are a factor. Below, I did this using a for loop, outputting each factor as it is found.
int number=15;
for(int i = 1; i <= number; i++){
if(number%i==0){
System.out.println("Found factor: " + i);
}
}
As Theo said in a comment on this post, you can also use number/2, and arbitrarily include 1 and number.
int number=2229348;
System.out.println("Found factor: " + 1);
for(int i = 2; i <= number/2; i++){
if(number%i==0){
System.out.println("Found factor: " + i);
}
}
System.out.println("Found factor: " + number);
You can iterate through the numbers from 2 to a/2 and check if the given number divides a, which is done using the % operator:
int a = 15;
System.out.print("Divisors of " + a + ": ");
for(int i = 2; i <= a/2; ++i) {
if(a % i == 0) {
System.out.print(i + " ");
}
}
System.out.println();
This code prints all of the divisors of a. Not that you most probably want to ignore 1, since it divides all integers. Moreover, you don't need to check the numbers until a, because no number bigger than a / 2 can actually divide a apart from a itself.
The while loop with default values of a=15 and multiple=2 is already in an infinite loop. You need to correct that and check for subsequent increments of multiple whenever a%multiple ! = 0
public class Factors {
public static void main(String[] args){
/**
int multiple1=2,d=0,a=15; //multiple to be rephrased as factor
while((a%multiple1 != 0)&&(a>=multiple1)){
multiple1++; //this will increment the factor and check till the number itself
//System.out.println(multiple1+" is not a factor of a");
}
if(multiple1==a){
d=1;
}
*commented the original code
*/
int factor=1,d=0,a=20; //multiple rephrased as factor
while(a/2>=factor){ //re-arranged your while condition
factor++;
if((a%factor==0))
d++; //increment factor count whenever a factor is found
}
System.out.println("Total number of factors of a : "+(d+2)); // 1 and 'a' are by default factors of number 'a'
}
}
To find all factors inlcuding 1 and the number itself you can do something like below:
//Iterate from 2 until n/2 (inclusive) and divide n by each number.
//Return numbers that are factors (i.e. remainder = 0). Add the number itself in the end.
int[] findAllFactors(int number) {
int[] factors = IntStream.range(1, 1 + number / 2).filter(factor -> number % factor == 0).toArray();
int[] allFactors = new int[factors.length+1];
System.arraycopy(factors,0,allFactors,0,factors.length);
allFactors[factors.length] = number;
return allFactors;
}
To find only prime factors you can do something like this:
//Iterate from 2 until n/2 (inclusive) and divide n by each number.
// Return numbers that are factors (i.e. remainder = 0) and are prime
int[] findPrimeFactors(int number) {
return IntStream.range(2, 1 + number/ 2).filter(factor -> number % factor == 0 && isPrime(factor)).toArray();
}
Helper method for primality check:
//Iterate from 2 until n/2 (inclusive) and divide n by each number. Return false if at least one divisor is found
boolean isPrime(int n) {
if (n <= 1) throw new RuntimeException("Invalid input");
return !IntStream.range(2, 1+n/2).filter(x -> ((n % x == 0) && (x != n))).findFirst().isPresent();
}
If you are not on Java 8 and/or not using Lambda expressions, a simple iterative loop can be as below:
//Find all factors of a number
public Set<Integer> findFactors(int number) {
Set<Integer> factors = new TreeSet<>();
int i = 1;
factors.add(i);
while (i++ < 1 + number / 2) {
if ((number % i) == 0) {
factors.add(i);
}
}
factors.add(number);
return factors;
}
public class Abc{
public static void main(String...args){
if(args.length<2){
System.out.println("Usage : java Abc 22 3");
System.exit(1);
}
int no1=Integer.parseInt(args[0]);
int no=Integer.parseInt(args[1]),temp=0,i;
for(i=no;i<=no1;i+=no){
temp++;
}
System.out.println("Multiple of "+no+" to get "+no1+" is :--> "+temp);
//System.out.println(i+"--"+no1+"---"+no);
System.out.println("Reminder is "+(no1-i+no));
}
}

What is the average digit total of a given integer?

I have to use a static method TotalAverage(int n) that would calculate the average digit total of the numbers 0 + 1 + 2 + .... + n. So that totalAverage(19) would be calculated as (0 + 1 + ... + 9 + 1 + ... + 10) / 20.0. I managed to do it for the most part using the following code:
public static double TotalAverage(int n) {
double total = 0;
int count = 0;
while (n >= 0) {
total += n % 10;
n = n - 1;
count++;
}
return total / count;
}
It works for numbers up to 9, but I get incorrect results for larger numbers. I realise that once the while statements gets to 10 % 10 it adds 0 to the total and not a 10, but I can't figure out how to do it correctly.
If you're looking to sum all digits of a number then the error in your code is
total += n % 10;
which only get the ones digit from n. Use some loop to get all digits from n without modifying it (because if you modify n your outer loop will break). Try:
int temp = n;
while(temp>0) {
total += temp % 10; //add next digit
temp /= 10;
}
You could use a separate method for digit sum. Something like this would work.
private static int digitSum(int a) {
return a < 10 ? a : a%10 + digitSum(a/10);
}
Then you can replace the line
total += n % 10
with
total += digitSum(n);

Categories