Optimize calculation of prime numbers [duplicate] - java

This question already has answers here:
Project Euler 3 - Highest Prime Factor
(2 answers)
Project Euler #3 takes forever in Java
(9 answers)
Closed 9 years ago.
I am trying Problem 3 from Project Euler and my algorithm is far too slow. Does anyone know how to optimize this? The number I am trying to calculate up to is 600851475143L. It takes forever to calculate this so I need a way to speed up the calculations.
LOGIC:
Go through all the numbers from 3 until that number-1
For each of these numbers check if they are prime by dividing them by all the numbers in between and if they dont divide by any of them then they are prime.
If prime then add them to an array.
public static void problem3(long number){
long number2 = number;
long sqrtNumber = (long)Math.sqrt(number2);
int indexNum = 1;
boolean isPrime = false;
int primeNums[] = new int[2];
primeNums[0] = 2;
//puts prime numbers into an array
for(int y = 3; y < sqrtNumber; y++){
isPrime=true;
for(int theNum = 2; theNum < y; theNum++){
//if y divides evenly by any number then it is not prime
if(y%theNum==0){
//dont store in array
isPrime=false;
break;
}
}
if(isPrime == true){
//add to array
System.out.println(y);
//put y in the array and exapnd the array
//System.out.println("y: " + y);
primeNums[indexNum] = y;
int[] newArray = new int[primeNums.length + 1];
System.arraycopy(primeNums, 0, newArray, 0, primeNums.length);
primeNums = newArray;
indexNum++;
}
}
********** UPDATE **************
I calculated up to the square root which sped up the calculations a lot but I also done something else which was to add a break statement in the forloop to break once I discovered the number was not prime. I edited the code above to reflect these changes.
My algorithm is still wrong for calculating the prime factors though so I will need to have a look at it and maybe raise a new question.

You don't have to divide by every number. You only have to divide by each prime number between 2 and the square root of your number.

The first thing you can do is only test possible factors up through the square root of the number that you're testing, because if you find a factor greater than the square root, then you should have found a factor less than the square root.
If you need additional performance, then use the Sieve of Eratosthenes. That allows you to use the results of previous primes to cut down the work on determining if larger numbers are prime.

Related

Adding Values to a List and Converting to BigInteger - Java [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm writing a code that determines the smallest integer that is a sequence of sevens followed by some number of zeros (possibly none) that is divisible by int n. Since this number can be massive, the return value should be a BigInteger.
My code so far has an if-else ladder that covers the case that if any int n is not divisible by two or five is guaranteed to only contain sevens (no zeros). In the case where int n is not divisible by two or five, my thought process was to continue adding sevens to a LinkedList in a while loop, until that list (converted to a BigInteger) is divisible by int n. The same logic goes for the case where int n is divisible by two or five, except a two for-loops would add seven and zero to the list.
My code is getting a runtime error when converting the list to a string and then to a BigInteger, specifically on the line BigInteger numBig = new BigInteger(str);. The error is: "java.lang.NumberFormatException: Zero length BigInteger (in java.math.BigInteger)" Also, I'm not quite sure the logic is sound for the case where int n is divisible by two or five.
You don't need BigInteger for this task. The idea is the following:
First you determine the number of required zeros. Since number composed of only sevens cannot be divided by 2 or 5, the number of zeros is equal to the maximum power of 2 or 5 in number n.
Now we have a number n which is not divisible by 2 or 5. Suppose that a remainder of the division of a number composed of m sevens by n is equal to r:
777...m-times..777 mod n = r
Then number composed of (m+1) sevens will have a remainder 10*r + 7, because
777..(m+1)-times...777 = 777...m-times...7 * 10 + 7
So you can just recalculate the remainder until it becomes zero.
public static BigInteger method(int n) {
int two;
for (two = 0; n % 2 == 0; two++) n /= 2;
int five;
for (five = 0; n % 5 == 0; five++) n /= 5;
int zeros = Math.max(two, five);
int sevens = 1;
int r = 7 % n;
while (r != 0) {
r = (r * 10 + 7) % n;
sevens++;
}
// Now just make a number of 'sevens' sevens and 'zeros' zeros:
StringBuilder result = new StringBuilder();
for (int i = 0; i < sevens; i++) {
result.append("7");
}
for (int i = 0; i < zeros; i++) {
result.append("0");
}
return new BigInteger(result.toString());
}
"Zero length BigInteger" means you're trying to create a BigInteger from something that has length of 0. The stack trace would tell you on which line exactly.
I would guess the bug is in your convert method. If you pass in an empty list, it tries to convert an empty string into BigInteger with new BigInteger("")
I don't know what your algorithm is supposed to do in this case. If for example you want to convert an empty list into the number zero, you can do:
if (res.isEmpty()) return BigInteger.ZERO;

Potentially simple math - Prime Number Code

I'm having some trouble getting my head around this program that's used an example in a guide book. It might be more of a math question than a coding question. Edited to include code!
// Find prime numbers between 2 and 100.
class Prime {
public static void main(String args[]) {
int x, y;
boolean isprime;
for(x=2; x < 100; x++) {
isprime = true;
for(y=2; y <= x/y; y++)
if((x%y) == 0) isprime = false;
if(isprime)
System.out.println(x + " is a prime number.");
}
}
}
Here's my understanding of the program:
Declare x (potential prime number) and y (a number to divide x by - if there is a division possible [other than by itself or 1] that yields no remainder, it's not a prime).
Declare a boolean value to hold whether the number is prime.
Create a for loop to test each and every number between 2 and 100
Default isprime boolean to true
Create a for loop to divide the prime by numbers between 2 and ??? (I don't understand the condition part of this for loop)
I tried to put in a system.out.print option here to show what x and y were at each iteration, but it then calculated non-prime numbers as prime numbers.
If you divide the prime by all these numbers and there is a number without a remainder, change boolean isprime to false.
If, after going through all values and all of them had a remainder, print that this is a prime number.
Its skipping numbers that do not make sense to check to see if they are factors. For example, if x = 49, it will check till y = 7 after which the condition will be false. Since it has already checked for all the numbers before 7, a higher factor is not possible that the square root of the number. So essentially, it checks for factors from 2 to square root of the number.

Get prime factors of a given number using Java [duplicate]

This question already has answers here:
Java Display the Prime Factorization of a number
(7 answers)
Closed 7 years ago.
I'm trying to get the prime factors of a given number using java.I was able to write following code.It's returning 2,5,10 for the prime factors of 100 while 10 is not a prime number and also it's returning 2,4,11 for the prime factors of 88 wile 4 is not a prime number.Can any one please explain what is the reason and how to fix it?.
import java.util.ArrayList;
import java.util.Random;
public class PrimeFactors {
public static void main(String[] args) {
Random randomGenarator = new Random();
int number = randomGenarator.nextInt(150);
System.out.println("Prime factors of :" + number);
findPrimeFactors(number);
}
private static void findPrimeFactors(int i) {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int n = 2; n <= i; n++) {
if (i % n == 0) {
list.add(n);
i /= n;
}
}
for (int n : list) {
System.out.println(n);
}
}
}
You aren't testing for multiple identical factors in your original number. E.g. when testing 88, you find a 2, but you miss the other 2s. (The number is now 44.) Then you skip to 3 and don't find it. Then you find 4, because 4 is a factor of 44.
You need to keep testing a factor until you no longer find it. Replace the if with while:
while (i % n == 0) {
list.add(n);
i /= n;
}
This way you'll find all 2s in the factor before anything else. Then when you get to other composite numbers such as 4 to test, you won't find them.
You are doing it wrong .
you need to remove all factors of a prime number before moving on .
for example (pseudo-code)
if i=100 ,
continue dividing it by 2 until it is non-divisible
i=100/2 =50 ,
then i=50/2 =25 then stop,
then look for next prime.
Hope it helps.

Exponent of a number in a product

I've encountered this problem on homework, we have to find the exponent of a given number(int) in a product of numbers(one-hundered long numbers). The problem is, we are not allowed to use the class BigInteger. I tried two aproaches, however both failed:
I wanted to store the prime factorisation of the given number(int-range) and then easily check for prime occurences in the list of long numbers. This method works for small numbers, but numbers close to 2**32 is not very efficient.
I looked for the gcd of the given number and every number in the list and if the gcd divided the given number then I divided them and then stored the gcd, do the same for the next number, found the gcd, multiplied it with the previous(possibly "leftover" gcd), etc. This method failed because when I look for gcd and for the 50th time I only find a factor of the given number, it's range is over the range of long.
Could someone please give me an advice on how to proceed or how to solve these obstacles.
I work in JAVA.
Well, this is basic math, but lets give you an anwer:
first you factorize the small given number, lets call it a. Now you have a as a factor of primes (with some kind of exponents).
Now you have to find out the exponents of these primes in the big number. Because they are primes, you can do this by finding the exponents in the factors of the product that you have and adding them together!
you'll do something like this:
// input from previous step
int[] a_primes;
int[] a_exponents;
int[] factors_of_b;
// output to next step
int[] b_exponents = new int[a_primes.length];
for (int i = 0; i < b_exponents.length; i ++) {
b_exponents = 0
}
for (int factor : factors_of_b) {
for (int i; i < a_primes.length; i++) {
if (factor % a_primes[i] == 0) {
factor /= a_primes[i];
b_exponents[i]++;
}
}
}
After this you have the exponents of every prime in b, that's also in a. Now you only need to find out how many times b's primes contain a's primes.
For that you'd do a minimum search:
// input from prevous step
int[] a_primes;
int[] a_exponents;
int[] b_exponents;
// final result
int result = b_exponents[0] / a_exponents[0];
for (int i = 0; i < a_primes.length; i++) {
int x = b_exponents[i] / a_exponents[i];
if (x < result) {
result = x;
}
}
And there you have it. This is just pseudo code, no error checking done, just yo you're clear on the concept.

Java Biasing Random Numbers in a Triangular Array

This question is an extension of Java- Math.random(): Selecting an element of a 13 by 13 triangular array. I am selecting two numbers at random (0-12 inclusive) and I wanted the values to be equal.
But now, since this is a multiplication game, I want a way to bias the results so certain combinations come up more frequently (like if the Player does worse for 12x8, I want it to come up more frequently). Eventually, I would like to bias towards any of the 91 combinations, but once I get this down, that should not be hard.
My Thoughts: Add some int n to the triangular number and Random.nextInt(91 + n) to bias the results toward a combination.
private int[] triLessThan(int x, int[] bias) { // I'm thinking a 91 element array, 0 for no bias, positive for bias towards
int i = 0;
int last = 0;
while (true) {
int sum = 0;
for (int a = 0; a < i * (i + 2)/2; a++){
sum += bias[a]
}
int triangle = i * (i + 1) / 2;
if (triangle + sum > x){
int[] toReturn = {last,i};
return toReturn;
}
last = triangle;
i++;
}
}
At the random number roll:
int sum = sumOfArray(bias); // bias is the array;
int roll = random.nextInt(91 + sum);
int[] triNum = triLessThan(roll);
int num1 = triNum[1];
int num2 = roll - triNum[0]; //now split into parts and make bias[] add chances to one number.
where sumOfArray just finds the sum (that formula is easy). Will this work?
Edit: Using Floris's idea:
At random number roll:
int[] bias = {1,1,1,...,1,1,1} // 91 elements
int roll = random.nextInt(sumOfBias());
int num1 = roll;
int num2 = 0;
while (roll > 0){
roll -= bias[num2];
num2++;
}
num1 = (int) (Math.sqrt(8 * num2 + 1) - 1)/2;
num2 -= num1 * (num1 + 1) / 2;
You already know how to convert a number between 0 and 91 and turn it into a roll (from the answer to your previous question). I would suggest that you create an array of N elements, where N >> 91. Fill the first 91 elements with 0...90, and set a counter A to 91. Now choose a number between 0 and A, pick the corresponding element from the array, and convert to a multiplication problem. If the answer is wrong, append the number of the problem to the end of the array, and increment A by one.
This will create an array in which the frequencies of sampling will represent the number of times a problem was solved incorrectly - but it doesn't ever lower the frequency again if the problem is solved correctly the next time it is asked.
An alternative and better solution, and one that is a little closer to yours (but distinct) creates an array of 91 frequencies - each initially set to 1 - and keeps track of the sum (initially 91). But now, when you choose a random number (between 0 and sum) you traverse the array until the cumulative sum is greater then your random number - the number of the bin is the roll you choose, and you convert that with the formula derived earlier. If the answer is wrong you increment the bin and update the sum; if it is right, you decrement the sum but never to a value less than one, and update the sum. Repeat.
This should give you exactly what you are asking: given an array of 91 numbers ("bins"), randomly select a bin in such a way that the probability of that bin is proportional to the value in it. Return the index of the bin (which can be turned into the combination of numbers using the method you had before). This function is called with the bin (frequency) array as the first parameter, and the cumulative sum as the second. You look up where the cumulative sum of the first n elements first exceeds a random number scaled by the sum of the frequencies:
private int chooseBin(float[] freq, float fsum) {
// given an array of frequencies (probabilities) freq
// and the sum of this array, fsum
// choose a random number between 0 and 90
// such that if this function is called many times
// the frequency with which each value is observed converges
// on the frequencies in freq
float x, cs=0; // x stores random value, cs is cumulative sum
int ii=-1; // variable that increments until random value is found
x = Math.rand();
while(cs < x*fsum && ii<90) {
// increment cumulative sum until it's bigger than fraction x of sum
ii++;
cs += freq[ii];
}
return ii;
}
I confirmed that it gives me a histogram (blue bars) that looks exactly like the probability distribution that I fed it (red line):
(note - this was plotted with matlab so X goes from 1 to 91, not from 0 to 90).
Here is another idea (this is not really answering the question, but it's potentially even more interesting):
You can skew your probability of choosing a particular problem by sampling something other than a uniform distribution. For example, the square of a uniformly sampled random variate will favor smaller numbers. This gives us an interesting possibility:
First, shuffle your 91 numbers into a random order
Next, pick a number from a non-uniform distribution (one that favors smaller numbers). Since the numbers were randomly shuffled, they are in fact equally likely to be chosen. But now here's the trick: if the problem (represented by the number picked) is solved correctly, you move the problem number "to the top of the stack", where it is least likely to be chosen again. If the player gets it wrong, it is moved to the bottom of the stack, where it is most likely to be chosen again. Over time, difficult problems move to the bottom of the stack.
You can create random distributions with different degrees of skew using a variation of
roll = (int)(91*(asin(Math.rand()*a)/asin(a)))
As you make a closer to 1, the function tends to favor lower numbers with almost zero probability of higher numbers:
I believe the following code sections do what I described:
private int[] chooseProblem(float bias, int[] currentShuffle) {
// if bias == 0, we choose from uniform distribution
// for 0 < bias <= 1, we choose from increasingly biased distribution
// for bias > 1, we choose from uniform distribution
// array currentShuffle contains the numbers 0..90, initially in shuffled order
// when a problem is solved correctly it is moved to the top of the pile
// when it is wrong, it is moved to the bottom.
// return value contains number1, number2, and the current position of the problem in the list
int problem, problemIndex;
if(bias < 0 || bias > 1) bias = 0;
if(bias == 0) {
problem = random.nextInt(91);
problemIndex = problem;
}
else {
float x = asin(Math.random()*bias)/asin(bias);
problemIndex = Math.floor(91*x);
problem = currentShuffle[problemIndex];
}
// now convert "problem number" into two numbers:
int first, last;
first = (int)((Math.sqrt(8*problem + 1)-1)/2);
last = problem - first * (first+1) / 2;
// and return the result:
return {first, last, problemIndex};
}
private void shuffleProblems(int[] currentShuffle, int upDown) {
// when upDown==0, return a randomly shuffled array
// when upDown < 0, (wrong answer) move element[-upDown] to zero
// when upDown > 0, (correct answer) move element[upDown] to last position
// note - if problem 0 is answered incorrectly, don't call this routine!
int ii, temp, swap;
if(upDown == 0) {
// first an ordered list:
for(ii=0;ii<91;ii++) {
currentShuffle[ii]=ii;
}
// now shuffle it:
for(ii=0;ii<91;ii++) {
temp = currentShuffle[ii];
swap = ii + random.nextInt(91-ii);
currentShuffle[ii]=currentShuffle[swap];
currentShuffle[swap]=temp;
}
return;
}
if(upDown < 0) {
temp = currentShuffle[-upDown];
for(ii = -upDown; ii>0; ii--) {
currentShuffle[ii]=currentShuffle[ii-1];
}
currentShuffle[0] = temp;
}
else {
temp = currentShuffle[upDown];
for(ii = upDown; ii<90; ii++) {
currentShuffle[ii]=currentShuffle[ii+1];
}
currentShuffle[90] = temp;
}
return;
}
// main problem posing loop:
int[] currentShuffle = new int[91];
int[] newProblem;
int keepGoing = 1;
// initial shuffle:
shuffleProblems( currentShuffle, 0); // initial shuffle
while(keepGoing) {
newProblem = chooseProblem(bias, currentShuffle);
// pose the problem, get the answer
if(wrong) {
if(newProblem > 0) shuffleProblems( currentShuffle, -newProblem[2]);
}
else shuffleProblems( currentShuffle, newProblem[2]);
// decide if you keep going...
}

Categories