Advice on speeding up prime number counter - java

How can I improve the logic of the following code? The purpose is to compute the number of primes from 1 to the user input (limitNo). The program works fine except it takes a while, more than the usual 1-3 secs, to generate a result for huge numbers like 99999.
public static int countPrime(int limitNo) {
int noOfTimes = 0, noOfRounds = 0; int o = 1;
while (o <= limitNo) {
for (int i = 1; i <= o; i++) {
if (o%i == 0) {
noOfRounds++;
}
}
if (noOfRounds == 2) {
noOfTimes++;
}
noOfRounds = 0;
o++;
}
return noOfTimes;
}

The code can be improved by
Separating some of the lines to make an isPrime() method.
Changing limits of the for loop so that if the condition is met that means the number is not prime.
public static boolean isPrime(int num) {
for ( int i = 2 ; i < num ; i++ ) {
if ( num % i == 0 ) {
return false;
}
}
return true;
}
Replacing the code in the method with isPrime() and change the start int o = 2 ;.
public static int countPrime(int limitNo) {
int noOfTimes = 0;
int o = 2;
while ( o <= limitNo ) {
if ( isPrime(o) ) {
noOfTimes++;
}
o++;
}
return noOfTimes;
}
Of course, there are better and more improvements like:
for ( int i = 2 ; i <= num/2 ; i++ )
for ( int i = 2 ; i <= Math.sqrt(num) ; i++ )

Related

Sieve of Eratosthenes (min and max)

I am using the Sieve of Eratosthenes algorithm to find the prime numbers in a range (from a min value to a max value). However, I cannot seem to get it to work if I include a min value.
Here is my code in Java:
protected static List<Integer> getSievePrimes(int a, int b) {
List<Integer> primeNumbers = new ArrayList();
boolean [] isComposite = new boolean [b + 1];
isComposite[1] = true;
// Mark all composite numbers
for (int i = 2; i <= b; i++) {
// for (int i = a == 1 ? 2 : a; i <= b; i++) {
if (!isComposite[i]) {
// 'i' is a prime number
//if (i >= a) {
primeNumbers.add(i);
//}
int multiple = 2;
while (i * multiple <= b) {
isComposite [i * multiple] = true;
multiple++;
}
}
}
return primeNumbers;
}
As you can see it currently only caters for the max value (b), and not the min value (a).
Question
How can I modify the method above to cater for both min and max?
public boolean isPrime( int test )
{
int k;
if( test < 2 )
return false;
else if( test == 2 )
return true;
else if( ( test > 2 ) && ( test % 2 == 0 ) )
return false;
else
{
for( k = 3; k < ( test/2 ); k += 2 )
{
if( test % k == 0 )
return false;
}
}
return true;
}
public void sieveOfEratosthenes( int first, int last )
{
boolean[ ] sieve = new boolean[ ( last - first ) + 1 ];
int count = 0;
int index = 0;
int cursor = first;
int end = last;
while( cursor <= end )
{
if( isPrime( cursor ) != sieve[ index ] )
{
System.out.println( cursor+" " );
count++;
}
cursor++;
index++;
}
cursor = first;
if( count == 0 )
System.out.println( "There are "+count+" primes from "+cursor+" to "+end+"." );
else if( count == 1 )
System.out.println( "is the "+count+" prime from "+cursor+" to "+end+"." );
else
System.out.println( "are the "+count+" primes from "+cursor+" to "+end+"." );
}

How do I assign array values with a while loop ?

I'm trying to make a while loop that iterates through every long number possible and add every prime number it encounters into an the primes array. Since the while loop is supposed to run until the length of primes is 200, I expect the primes array to be filled with the first 200 prime numbers. Instead I get all zeroes. I have successfully gotten 20 rows of 10 characters each with a space in between them. How may I get them to be the actual prime numbers though?
public class PrimeGenerator {
public static void main(String[] args) {
long primes[] = new long[200];
while (primes.length > 200){
for(long y = 2; y < Long.MAX_VALUE; y++) {
int primeCounter = 0;
if (isPrime(y) == true){
primes[primeCounter] = y;
primeCounter++;
}
}
}
for (int i = 0; i < 20; i++) {
int primeCounter = 0;
for(int p = 0; p < 10; p++) {
System.out.print(primes[primeCounter] + " ");
primeCounter++;
}
System.out.println();
}
}
public static boolean isPrime(long number) {
if (number % 2 == 0)
return false;
if (number == 2)
return true;
for(int x = 3; x*x <= number; x+=2) {
if (number%x == 0)
return false;
}
return true;
}
}
primes.length is always 200 so the while loop is never entered.
The while loop is useless. Just add a condition to the for loop that would exit when the entire array has been assigned. Also move the initialization of primeCounter to be outside the for loop. Otherwise all the primes will be assigned to primes[0].
long primes[] = new long[200];
int primeCounter = 0;
for(long y = 2; y < Long.MAX_VALUE && primeCounter < 200; y++) {
if (isPrime(y) == true){
primes[primeCounter] = y;
primeCounter++;
}
}
for (int i = 0; i < primes.length; i++) {
System.out.print(primes[i]);
if ((i+1) % 10 == 0)
System.out.println();
}
EDIT :
As Sweeper commented, you should also fix your isPrime method, since it returns false for 2 :
public static boolean isPrime(long number) {
if (number == 2)
return true;
if (number % 2 == 0)
return false;
for(int x = 3; x*x <= number; x+=2) {
if (number%x == 0)
return false;
}
return true;
}
this code down
long primes[] = new long[200];
while (primes.length > 200){
means
while (200 > 200){
or the same as
while (false){
so your loop is NEVER executed!
because you did:
while (primes.length > 200)
and the length of the array is always 200,you never get into the while loop , and the zero in the array are coming because when you create array of "long" it initialized him with zeros
Firstly, the length of an array doesn't change. So, when you are testing for primes.length > 200 this will always be false, and the loop is never even entered. Therefore all values in the array are left at the default value of 0.
For doing this I would doing something like the following:
int primeCounter = 0;
long current = 0L;
while(primeCounter < primes.length){
if(isPrime(current)){
primes[primeCounter] = current;
primeCounter++;
}
current++;
}
An array's length never changes. If you declared an array to have a length of 200, it will always have a length of 200. Because of this, your while loop is never executed, not even once.
There are a lot of other errors in the code, so I tried to create a solution with as few changes as possible:
public static void main(String[] args) {
int primeCounter = 0;
long nextPossiblePrime = 2;
long primes[] = new long[200];
while (primeCounter < 200) {
for (long y = nextPossiblePrime; y < Long.MAX_VALUE; y++) {
if (isPrime(y) == true) {
primes[primeCounter] = y;
primeCounter++;
nextPossiblePrime = y + 1;
break;
}
}
}
primeCounter = 0;
for (int i = 0; i < 20; i++) {
for (int p = 0; p < 10; p++) {
System.out.print(primes[primeCounter] + " ");
primeCounter++;
}
System.out.println();
}
}
public static boolean isPrime(long number) {
if (number == 2 || number == 3)
return true;
if (number % 2 == 0)
return false;
for (int x = 3; x * x <= number; x += 2) {
if (number % x == 0)
return false;
}
return true;
}
The first problem is that you created two primeCounters, which is not needed. I removed the extra one and moved the scope of it to the scope of the method. The next problem is that your first for loop doesn't remember the prime number that it is on and it doesn't stop when it has found one so it will keep adding the 200th prime to the array. I fixed this by adding a nextPossiblePrime variable that stores what number should the program check next. The last problem is that your isPrime method is written incorrectly. I fixed it for you!
Here's another (cleaner) solution, which still uses a while loop:
public static void main(String[] args) {
ArrayList<Long> primes = new ArrayList<>();
long y = 2;
while (y < Long.MAX_VALUE && primes.size() < 200) {
if (isPrime(y) == true){
primes.add(y);
}
y++;
}
for (int i = 0; i < primes.size(); i++) {
System.out.print(primes.get(i) + " ");
if ((i+1) % 10 == 0)
System.out.println();
}
}
public static boolean isPrime(long number) {
if (number == 2 || number == 3)
return true;
if (number % 2 == 0)
return false;
for (int x = 3; x * x <= number; x += 2) {
if (number % x == 0)
return false;
}
return true;
}

Zigzag conversion

Question is : The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: "PAHNAPLSIIGYIR"
I have written below code, appearantly it works fine, but I might miss some corner cases. Could you help me to find all the corner cases for this question on my answer?
public static String zigZagConversion(String s , int rowNum){
if (s == null){
throw new IllegalArgumentException();
}
if (rowNum == 1){
return s;
}
StringBuilder str = new StringBuilder();
int step = 2 * rowNum - 2 ;
for (int i = 0 ; i < rowNum ; i++){
if( i == 0 || i == rowNum -1){
for (int j = i ; j < s.length() ; j +=step){
str.append(s.charAt(j));
}
}
else{
int step2 = 2* (rowNum - i - 1);
int step3 = step - step2;
int k = i;
boolean flag = true;
while (k < s.length()){
str.append(s.charAt(k));
if(flag){
k += step2;
flag = false;
}
else{
k +=step3;
flag = false;
}
}
}
}
return str.toString();
}
It gives incorrect output for "PAYPALISHIRING", 4
P I N
A L S I G
Y A H R
P I
So the correct answer should be PINALSIGYAHRPI.
But your program gives PINALIGYAIHRNPI:
an "S" is missing, one extra "I" and one extra "N".
Your revised version is still incorrect, it gives PINALSIIGYAHNPI.
The problem is in the while loop in the middle.
You need to alternate the step counting,
setting the flag on and off.
Your mistake was to only set it off once, and never back on again.
str.append(s.charAt(k));
if (flag) {
k += step2;
flag = false;
} else {
k += step3;
flag = true;
}
With this correction, I believe your solution is correct. (I also added a minor improvement there, extracting the common str.append(s.charAt(k)); from the if-else branches.
My solution on leetcode forum:
https://leetcode.com/problems/zigzag-conversion/discuss/549451/Java-Solution-O(n)-with-algorithm
The mathematic algorithm for zigzag is:
originalDiff = numRows * 2 - 2;
If -> 'currRow' equals First or last lines
use the originalDiff (numRows * 2 - 2)
Else ->
For each new line:
upperDiff += 2,
lowerDiff -=2
Examples:
numRows =2 -> originalDiff = 2
PYAIHRN
APLSIIG
3 -> 4
P A H N
A P L S I I G
Y I R
numRows = 4 -> originalDiff = 6
P I N
A L S I G
Y A H R
P I
numRows = 5 -> originalDiff = 8
P H
A SI
Y I R
P L I G
A N
*/
My solution:
class Solution {
public String convert(String s, int numRows) {
if(numRows == 1) {
return s;
}
String newString = "";
int originalDiff = numRows * 2 - 2;
int diff = originalDiff;
int upperDiff = 0;
boolean isGoingDown = true;
int currIndex = 0;
int currRow = 0;
int startingIndex = 0;
for(int i = 0; i < s.length(); i++) {
System.out.println(currIndex);
newString += s.charAt(currIndex);
if(currRow == 0 || currRow == numRows - 1) {
currIndex += originalDiff;
} else {
if(isGoingDown) {
currIndex += diff;
isGoingDown = !isGoingDown;
} else {
currIndex += upperDiff;
isGoingDown = !isGoingDown;
}
}
if(currIndex >= s.length()) {
currRow++;
diff -= 2;
upperDiff += 2;
currIndex = currRow;
isGoingDown = true;
}
if(currRow == numRows) {
i = s.length();
}
}
return newString;
}
}
Zigzag conversion from leetcode in Javascript
Solution
const zigzag = (str, num) => {
if (num === 1) {
return str;
}
let check = true;
let result = [];
let i = 0;
while (i < str.length) {
result.push([]);
let j = 0;
while (j < num) {
if (check){
result[result.length-1].push(str[i]);
i++;
} else {
if (j == 0) {
result[result.length-1].push(null);
} else if (j === num-1) {
result[result.length-1].unshift(null);
} else {
result[result.length-1].unshift(str[i]);
i++;
}
}
j++;
}
check = !check;
}
let zigzag = [];
for (let k = 0; k < num; k++){
for(let l = 0; l < result.length; l++) {
zigzag.push(result[l][k]);
}
}
return zigzag.join("");
}
Example Input
zigzag("ABCD", 3)
Output
ABDC
Run
https://repl.it/#VinitKhandelwal/zigzag-conversion-javascript
Using HashMap
public String convert(String s, int numRows) {
if (numRows == 1){
return s;
}
StringBuilder result = new StringBuilder();
Map<Integer, StringBuilder> map = new HashMap<>();
for (int i = 0; i < numRows; i++) {
map.put(i,new StringBuilder());
}
int it = 0;
boolean flip = true;
for (int i = 0; i < s.length(); i++) {
if (flip) {
if(it<s.length()){
map.get(it).append(s.charAt(i));
it++;
}
} else {
map.get(it).append(s.charAt(i));
it--;
}
if (it + 1 == numRows || it == 0)
flip = !flip;
}
for (Map.Entry entry: map.entrySet()) {
result.append(entry.getValue());
}
return result.toString();
}
My Solution is traversing the string in the same way it is said in the problem, it is better to make string array of size numrows and the rest is storing the string character as it is in the logic,
you can keep the index and when that index is 0 i.e at the starting then we have to go till the end of the row and then except for first and last row, every array will have diagonal element.
So after traversing till the end then assign index = numrows - 2 and save in the respective array string and decrease and do the same till index >0 and then again traverse till the end row, do this and when we reach the end of the string then break from the loop.
and then concate all the string of string array in a new res string.
class Solution {
public String convert(String s, int n) {
if(n==1 || n>=s.length())
return s;
String[] a = new String[n]; //string array
int ind=0; // index for the string array
boolean flag=true;
int cnt=0; //to keep the counter till where we have traversed the string
while(true && flag)
{
if(ind==0)
{
for(int i=0;i<n;i++)
{
a[i] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
} // here it has reached the end so we assign here
ind = n-2;
}
else if(ind>0 && ind<n && flag)
{
a[ind] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
ind--; // to move diagonally up
}
}
String res = new String("");
for(int i=0;i<a.length;i++)
{
// System.out.println(a[i].substring(4));
res += a[i].substring(4);
}
return res;
}
}
Following is the simple solution.
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows <= 1:
return s
res = ""
p = numRows * 2 - 2
temp = p
for i in range(0,numRows):
index = i
flag = 0
while index < len(s):
res = res + s[index]
if i == 0 or i == numRows-1:
index = index + p
else:
if flag == 0:
index = index + temp
flag = 1
else:
index = index + p-temp
flag = 0
temp = temp - 2
return res
zigzag-conversion Complete JavaScript-based solution
Created an Array of an array of row lengths. The main motive is to arrange characters in 2D array form and concat string row-wise.
var convert = function(s, numRows) {
let array =[],c=0,str='';
for(let row =0; row<numRows ; row++) {
array[row] = new Array();
}
while(c < s.length) {
for(let row =0; row<numRows ; row++) {
if((row+1)%numRows ==0) {
array[row].push(s[c]);
c++;
break;
} else {
array[row].push(s[c]);
c++;
}
}
for(let rr = numRows-2 ; rr>0;rr--) {
array[rr].push(s[c]);
c++;
}
}
for(let row =0; row<numRows ; row++) {
for(let i=0;i<array[row].length;i++){
if(array[row][i]){
str+=array[row][i]
}
}
}
return str
};
convert("PAYPALISHIRING",3)

program is not reading the for loop

import java.lang.Math;
import java.text.*;
public class Problem4 {
public static int reverse( int n ) {
int i = 0;
while ( n != 0 ) {
int r = n % 10;
i = (i * 10) + r;
n /= 10;
}
return i;
}
public static boolean isPalindrome( int n ) {
return ( n == reverse(n) ) ? true : false;
}
public static void main( String[] args ) {
int jHi=0, jlow=0, dec=0;
int P=1;
int i=999, j=1;
for ( i=999; i <= 100; i = i - 1 )
{
if ( i % 11 == 0 )
{
jHi = 999;
jlow = 100;
dec = 1;
}
else
{
jHi = 990;
jlow = 100;
dec = 11;
}
for ( j = jHi; j >= jlow; j = j - dec )
{
P = i * j;
if ( isPalindrome(P) )
{
break;
}
else
{
continue;
}
}
}
System.out.println( "Largest Palindrome is " + P );
}
}
Your for loop does not execute because the loop condition i<=100 is never met with an initial value of i=999.
Since you want your loop to count downwards, change
for(i=999; i<=100; i=i-1)
to
for(i=999; i>=100; i=i-1)
i=999; i<=100; i=i-1 --change it as i=999; i>=100; i=i-1
This means "start with i as 999 and do it while i is less or equal than 100 decreasing i by one in each step":
for(i=999; i<=100; i=i-1)
Your for loop will not execute for very first time and hence no operation. Always use proper condition.

Testing if a number inside an Array is a duplicate and then removing it

So my Problem is i have to list the prime factors of a number inside an Array and the power of that prime factor in another one on the same positions inside their given array(so if you want the primefactors of 60 i would need to return an array with contents like this: primes: {2, 3, 5} powers {2, 1, 1} => (2*2)*(3*1)*(5*1) = 60.
I now have the following code to determine the duplicates inside the primes Array but how can i now instead of printing them to the console save them in another variable to then use them for the powers Array?
long current = primes[0];
boolean found = false;
for( int i = 0; i < primes.length; i++) {
if( current == primes[i] && !found) {
found = true;
}
else if( current != primes[i] ) {
System.out.print(" " + current);
current = primes[i];
found = false;
}
The full code would then be:
public class Algebra {
public static long [][] primfaktorzerlegung(long n){
int position = 0;
long[] primes = new long [0];
long [] powers = new long [0];
while(n%2 == 0) {
primes[position] = 2;
position++;
n = n / 2;
}
for (int i = 3; i <= Math.sqrt(n); i+= 2)
{
while (n%i == 0)
{
n /= i;
}
}
long current = primes[0];
boolean found = false;
for (int i = 0; i < primes.length; i++) {
if (current == primes[i] && !found) {
found = true;
} else if (current != primes[i]) {
current = primes[i];
found = false;
}
}
long[][] z = {primes,powers};
return z;
}
}
It's obviously unfinished but to show the whole thing i post it anyways.
You want the frequency of each prime, and there’s a standard way to do this in java. Also, since you don’t know how many primes there will be you’re better to use a List.
But you don’t even need to use either of those, just use a Map<Long, Long> and accumulate both primes and powers in one pass:
Map<Long, Long> primePowers = new LinkedHashMap<>();
for (int i = 2; i <= Math.sqrt(n); i+= 2) {
while (n%i == 0) {
primePowers.put(i, primePowers.getOrDefault(i, 0L) + 1);
n /= i;
}
}
// convert the Map to the return value
long[] primes, powers = new long[primePowers.size()];
int i = 0;
for (Map.Entry<Long, Long> entry : primePowers.entrySet()) {
primes[i] = entry.getKey();
powers[i] = entry.getValue();
}
return new long[][]{primes,powers};
FYI a LinkedHashMap iterates over its entries in insert order.
As a design point, the return type long[][] is not a good choice. Any situation where 2 arrays must agree on their elements aligning is poor design.
Code:
import java.util.ArrayList;
import java.util.List;
public class Primes {
static long getPowerOf( long power, long n, List<Long> primes, List<Long> powers ) {
if(( n % power ) == 0 ) {
long count = 0L;
while(( n % power ) == 0 ) {
++count;
n = n / power;
}
primes.add( power );
powers.add( count );
}
return n;
}
static long[][] getPrimes( long n ) throws Exception {
final List<Long> primes = new ArrayList<>();
final List<Long> powers = new ArrayList<>();
n = getPowerOf( 2, n, primes, powers );
for( long i = 3; n > 1; i += 2 ) {
n = getPowerOf( i, n, primes, powers );
}
if( n > 1 ) {
throw new Exception( "More primes needed" );
}
final long[][] result = new long[2][];
result[0] = new long[primes.size()];
result[1] = new long[powers.size()];
for( int i = 0; i < primes.size(); ++i ) {
result[0][i] = primes.get( i );
}
for( int i = 0; i < powers.size(); ++i ) {
result[1][i] = powers.get( i );
}
return result;
}
static void showPrimes( long[] primes, long[] powers ) {
boolean tail = false;
for( int i = 0; i < primes.length; ++i ) {
if( powers[i] > 0 ) {
if( tail ) {
System.out.print( " + " );
}
else {
tail = true;
}
System.out.print( primes[i] + "x" + powers[i]);
}
}
System.out.println();
}
public static void main( String[] args ) throws Exception {
final long[][] result = getPrimes( 2*2*3*5*5*7*23*23*23 );
showPrimes( result[0], result[1] );
}
}
Output:
2x2 + 3x1 + 5x2 + 7x1 + 23x3
Notes:
Using a class in place of long[][] for primes and powers will be better, like this:
class PrimeUsage {
long prime;
long power;
}
PrimeUsage[] or List<PrimeUsage> or Set<PrimeUsage>

Categories