I'm making a class for the Straight category of Yahtzee. Yahtzee has 2 options for a straight, small or larger. A small straight would be a sequence of 4 consecutive die faces such as 1, 2, 3, 4. A larger straight would be a sequence of 5 consecutive die faces. If a player get a small straight they receive 30 points and if they get a larger that is 40 points.
My Straight class inherits from a class called Scores and only uses one method called getDiceScore. The getDiceScore will accept an argument of type DieInterface. I will attach the DieInterface interface code below.
I just want to make sure the logic of my code is sound for this project I am working on.
public interface DieInterface
{
public static String[] dieFaces =
{"+---+\n| |\n| o |\n| |\n+---+",
"+---+\n|o |\n| |\n| o|\n+---+",
"+---+\n|o |\n| o |\n| o|\n+---+",
"+---+\n|o o|\n| |\n|o o|\n+---+",
"+---+\n|o o|\n| o |\n|o o|\n+---+",
"+---+\n|o o|\n|o o|\n|o o|\n+---+"};
public static String toDieString(DieInterface aDie)
{
return dieFaces[aDie.getFaceValue()-1];
}
// Do not modify above this line
public static String toDiceString(DieInterface[] dice)
{
StringBuilder result = new StringBuilder();
String sideBySide = "";
String die1 = dieFaces[dice[0].getFaceValue()-1];
String die2 = dieFaces[dice[1].getFaceValue()-1];
String die3 = dieFaces[dice[2].getFaceValue()-1];
String die4 = dieFaces[dice[3].getFaceValue()-1];
String die5 = dieFaces[dice[4].getFaceValue()-1];
//String die6 = dieFaces[dice[5].getFaceValue()-1];
String splitter = die1 + "\n" + die2 + "\n" + die3 + "\n"+ die4 + "\n"+ die4+ "\n"+ die5;
String [] temp = splitter.split("\n");
for(int i = 0; i < (temp.length/6); i++)
{
result.append(temp[0*(5)+i] + " " + temp[1*(5)+i] + " " + temp[2*(5)+i] +" " + temp[3*(5)+i] +" "+ temp[4*(5)+i] +"\n");
}
return result.toString();
}
// Do not modify below this line
public int roll();
public int getFaceValue();
}
public class Straight extends Scores
{
protected String name;
protected int numConsecutiveFaces; // indicates how many consecutive faces that a player should have to satisfy this scoring category
public Straight(String aName, int numConsecutiveFaces)
{
super(aName);
this.numConsecutiveFaces = numConsecutiveFaces;
}
public int getDiceScore(DieInterface[] dice)
{
boolean ones = false;//determines that only one side of a die appeared once
int[] straight = new int[numConsecutiveFaces]; // array used to store numbers in the correct straight format
int [] counter = {0, 0, 0, 0, 0 ,0}; //using to track how many times a die appeared
//looping through dice array to determine how many times a die appeared
for(int i = 0; i < dice.length; i++)
{
counter[dice[i].getFaceValue()-1]++;
}
//sorting the die in sequential order
sort(counter);
//determining that a die only appeared once and they are no larger than by one value. ex 1, 2, 3, 4, 5 not 2, 3, 5, 6
for(int i = 0; i < counter.length; i++)
{
if(counter[i] == 1 && byAFactorOfOne(counter, counter) == true)
{
ones = true;
byAFactorOfOne(counter, counter);
counter[i] = straight[i];
}
}
//if 4 die in a row are in correct sequential order return 30 points back
if(straight[numConsecutiveFaces] == 4)
return 30;
//if 5 die in a row are in correct sequential order return 40 points back
else if(straight[numConsecutiveFaces] == 5)
return 40;
else
return 0;
}
private void sort(int[] counter)
{
for (int i = 0; i <counter.length; i++)
{
for (int j = 0; j < counter.length - i - 1; j++)
{
if (counter[j] > counter[j + 1])
{
int temp = counter[j];
counter[j] = counter[j + 1];
counter[j + 1] = temp;
}
}
}
}
private boolean byAFactorOfOne(int[] counter, int[] counter2)
{
int value;
int counting = 0;
boolean repeat = true;
int i = 0;
while(repeat && counting < counter.length)
{
value = counter[i] - counter[i + 1];
i++;
if(value != 1)
{
repeat = false;
return false;
}
counting ++;
}
return true;
}
}
First of all, the following code won't work:
if(straight[numConsecutiveFaces] == 4)
... because you aren't setting any values into the straight array.
You are counting the number of times each die value is displayed:
//looping through dice array to determine how many times a die appeared
for(int i = 0; i < dice.length; i++)
{
counter[dice[i].getFaceValue()-1]++;
}
... and the index of the array is the value on the die. This makes sense.
But as soon as you sort these values, you lose which die value each count represents. Don't do this:
//sorting the die in sequential order
sort(counter);
If you did that, you no longer know which die value was displayed n times.
Instead of sorting counter, what you need to do is look for 4 or 5 consecutive entries that are not 0. In other words, 4 or 5 consecutive entries that had at least one die showing that value.
You can take some shortcuts:
If counter[3] or counter[4] is zero, it's not possible for either a 4-length or 5-length straight
If counter[2] and counter[5] are both 0, it's not possible for a 5-length straight
If there are any counter values > 1, then it's not possible for a 5-length straight to have occurred
And if there are more than 1 counter values > 1, then it's not possible for a 4-length straight to have occurred.
A simpler way to solve this problem that doesn't involve counting values or sorting (uses binary values to represent the presence of a die value):
public class Straight extends Scores
{
static final int valueForOneToFour = Math.pow(2, 0) + Math.pow(2, 1) + Math.pow(2, 2) + Math.pow(2, 3);
static final int valueForTwoToFive = Math.pow(2, 1) + Math.pow(2, 2) + Math.pow(2, 3) + Math.pow(2, 4);
static final int valueForThreeToSix = Math.pow(2, 2) + Math.pow(2, 3) + Math.pow(2, 4) + Math.pow(2, 5);
static final int valueForOneToFive = Math.pow(2, 0) + Math.pow(2, 1) + Math.pow(2, 2) + Math.pow(2, 3) + Math.pow(2, 4);
static final int valueForTwoToSix = Math.pow(2, 1) + Math.pow(2, 2) + Math.pow(2, 3) + Math.pow(2, 4) + Math.pow(2, 5);
...
public int getDiceScore(DieInterface[] dice)
{
// calculate a binary representation of the values available
int occurrenceValue = 0;
for (int i = 0; i < 6; i++) {
occurrenceValue |= Math.pow(2, dice[i] - 1));
}
if (occurrenceValue & valueForOneToFive == valueForOneToFive
|| occurrenceValue & valueForTwoToSix == valueForTwoToSix) {
return 40;
}
if (occurrenceValue & valueForOneToFour == valueForOneToFour
|| occurrenceValue & valueForTwoToFive == valueForTwoToFive
|| occurrenceValue & valueForThreeToSix == valueForThreeToSix) {
return 30;
}
return 0;
}
}
I am trying to implement the Fibonacci sequence without using the BigInteger Class import, hence I rewrite my own add method, and I spent two days on it, But I don't know why the answer of the first 6 numbers is correct and the rest of the answers are the reverse of the correct one(eg. n = 7, my answer: 31 the correct one: 13; n = 15, my answer = 016, correct one= 610), and when n becomes greater, the answer gets totally wrong(not even the reversed of the correct one. This happened when n >= 25).
Any advice would be appreciated!
The following is my output:
The 0th Fibonacci number is :
0
The 1th Fibonacci number is :
1
The 2th Fibonacci number is :
1
The 3th Fibonacci number is :
2
The 4th Fibonacci number is :
3
The 5th Fibonacci number is :
5
The 6th Fibonacci number is :
8
The 7th Fibonacci number is :
31
The 8th Fibonacci number is :
12
The 9th Fibonacci number is :
43
The 10th Fibonacci number is :
55
The 11th Fibonacci number is :
98
The 12th Fibonacci number is :
441
The 13th Fibonacci number is :
332
The 14th Fibonacci number is :
773
The 15th Fibonacci number is :
016
The 16th Fibonacci number is :
789
The 17th Fibonacci number is :
7951
The 18th Fibonacci number is :
4852
The 19th Fibonacci number is :
1814
The 20th Fibonacci number is :
5676
The 21th Fibonacci number is :
64901
The 22th Fibonacci number is :
11771
The 23th Fibonacci number is :
75682
The 24th Fibonacci number is :
86364
The 25th Fibonacci number is :
52047
The 26th Fibonacci number is :
393021
The 27th Fibonacci number is :
814491
The 28th Fibonacci number is :
118413
The 29th Fibonacci number is :
922905
The 30th Fibonacci number is :
040428
And the following is my code:
package com.example.helloworld;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Fibonacci_Recursive{
public static void main(String[] args) {
long start = System.nanoTime();
long time = 0L;
for(int i = 0; time <= 60L; i++)
{
Fibonacci_Recursive fr = new Fibonacci_Recursive(i);
time = ((System.nanoTime() - start) / 1000_000_000);
}
}
private Fibonacci_Recursive(int n){
System.out.println("The " + n + "th Fibonacci number is :");
if (n <= 1){
System.out.println(n);
}
else {
int[] finalResult = getF(n);
String st = "";
for (int i = 0; i < finalResult.length; i++){
st = finalResult[i] + st;
}
System.out.println(st);
}
}
private int[] getF(int n){
int[] head = new int[1];
if (n <= 1) {
head[0] = n;
return head;
}
return add(getF(n - 1), getF(n - 2));
}
private int[] add(int[] s1, int[] s2){
int carrier = 0;
ArrayList<Integer> result = new ArrayList<>();
int[] array1 = s1;
int[] array2 = s2;
array1 = reverseGeneralArray(array1);
array2 = reverseGeneralArray(array2);
int min = array2.length;
int min2 = array1.length;
if(min2 > min) {
for (int i = 0; i < min; i++) {
int x = array1[i] + array2[i];
result.add((x + carrier) % 10);
carrier = x / 10;
}
for (int j = 0; j <= min2 - min - 1; j++) {
int index = min;
result.add((array1[index] + carrier) % 10);
carrier = (array1[index] + carrier) / 10;
index++;
}
if (carrier > 0) {
result.add(carrier);
}
Collections.reverse(result);
return convertIntegers(result);
}
else if(min2 < min)
{
for(int i = 0; i < min2; i ++){
int x = array1[i] + array2[i];
result.add((x + carrier) % 10);
carrier = x / 10;
}
for(int j = 0; j <= min - min2 - 1; j++){
int index = min2;
result.add((array2[index] + carrier) % 10);
carrier = (array2[index] + carrier) / 10;
index++;
}
if (carrier > 0) {
result.add(carrier);
}
Collections.reverse(result);
return convertIntegers(result);
}else {
for (int i = 0; i < min; i++) {
int x = array1[i] + array2[i];
result.add((x + carrier) % 10);
carrier = x / 10;
}
if (carrier > 0) {
result.add(carrier);
}
Collections.reverse(result);
return convertIntegers(result);
}
}
private static int[] convertIntegers(ArrayList<Integer> integers)
{
int[] ret = new int[integers.size()];
for (int i=0; i < integers.size(); i++)
{
ret[i] = integers.get(i);
}
return ret;
}
private int[] reverseGeneralArray(int[] x){
int[] newX = new int[x.length];
for(int i = 0; i < x.length; i++){
newX[i] = x[x.length - i -1];
}
return newX;
}
}
You miss building the result, you concatenate wrong (reverse) way String st from the int[] finalResult:
private Fibonacci_Recursive(int n) {
...
for (int i = 0; i < finalResult.length; i++) {
//Replaced st = finalResult[i] + st by
st = st + finalResult[i];
}
...
}
Extra: Consider, when concatenating strings in a loop, since concatenation copies the whole string, to use StringBuilder:
StringBuilder st = new StringBuilder();
for (int i = 0; i < finalResult.length; i++) {
st.append(finalResult[i]);
}
Update: Starting from 25, an error becomes evident: the carrier is not right when the sum of two digits is equal to 10 (74025 instead of 75025). The bug is at the add method,where the carrier should be calculated as:
carrier = (x + carrier) / 10;
i.e.: you have to take into account previous carrier.
I need to design an algorithm to find the maximum value I can get from (stepping) along an int[] at predefined (step lengths).
Input is the number of times we can "use" each step length; and is given by n2, n5 and n10. n2 means that we move 2 spots in the array, n5 means 5 spots and n10 means 10 spots. We can only move forward (from left to right).
The int[] contains the values 1..5, the size of the array is (n2*2 + n5*5 + n10*10). The starting point is int[0].
Example: we start at int[0]. From here we can move to int[0+2] == 3, int[0+5] == 4 or int[0+10] == 1. Let's move to int[5] since it has the highest value. From int[5] we can move to int[5+2], int[5+5] or int[5+10] etc.
We should move along the array in step lengths of 2, 5 or 10 (and we can only use each step length n2-, n5- and n10-times) in such a manner that we step in the array to collect as high sum as possible.
The output is the maximum value possible.
public class Main {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
}
}
This is an answer in pseudocode (I didn't run it, but it should work).
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2step = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5step = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10step = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2step, 5step, 10step);
return dp[id][2stepcount][5stepcount][10stepcount];
}
Call dp(0,0,0,0) and the answer is in dp[0][0][0][0].
If you wanna go backwards, then you do this:
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1 || id < 0) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2stepForward = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepForward = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepForward = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
int 2stepBackward = 2stepcount < max2stepcount? dp(id - 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepBackward = 5stepcount < max5stepcount? dp(id - 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepBackward = 10stepcount < max10stepcount? dp(id - 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2stepForward, 5stepForward, 10stepForward, 2stepBackward, 5backForward, 10backForward);
return dp[id][2stepcount][5stepcount][10stepcount];
}
But your paths don't get fulled explored, because we stop if the index is negative or greater than the array size - 1, you can add the wrap around functionality, I guess.
this is a solution but i am not sure how optimal it is !
i did some optimization on it but i think much more can be done
I posted it with the example written in question
import java.util.Arrays;
import java.util.Random;
public class FindMax {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static int findMaxValue(int n2, int n5, int n10, int pos, int[] pokestops) {
System.out.print("|");
if (n2 <= 0 || n5 <= 0 || n10 <= 0) {
return 0;
}
int first;
int second;
int third;
if (pokestops[pos] == 5 || ((first = findMaxValue(n2 - 1, n5, n10, pos + 2, pokestops)) == 5) || ((second = findMaxValue(n2, n5 - 1, n10, pos + 5, pokestops)) == 5) || ((third = findMaxValue(n2, n5, n10 - 1, pos + 10, pokestops)) == 5)) {
return 5;
}
return Math.max(Math.max(Math.max(first, second), third), pokestops[pos]);
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
int max = findMaxValue(n2, n5, n10, 0, pokestops);
System.out.println("");
System.out.println("Max is :" + max);
}
}
You need to calculate following dynamic programming dp[c2][c5][c10][id] - where c2 is number of times you've stepped by 2, c5 - by 5, c10 - by 10 and id - where is your current position. I will write example for c2 and c5 only, it can be easily extended.
int[][][][] dp = new int[n2 + 1][n5 + 1][pokestops.length + 1];
for (int[][][] dp2 : dp) for (int[][] dp3 : dp2) Arrays.fill(dp3, Integer.MAX_VALUE);
dp[0][0][0] = pokestops[0];
for (int c2 = 0; c2 <= n2; c2++) {
for (int c5 = 0; c5 <= n5; c5++) {
for (int i = 0; i < pokestops.length; i++) {
if (c2 < n2 && dp[c2 + 1][c5][i + 2] < dp[c2][c5][i] + pokestops[i + 2]) {
dp[c2 + 1][c5][i + 2] = dp[c2][c5][i] + pokestops[i + 2];
}
if (c5 < n5 && dp[c2][c5 + 1][i + 5] < dp[c2][c5][i] + pokestops[i + 5]) {
dp[c2][c5 + 1][i + 5] = dp[c2][c5][i] + pokestops[i + 5];
}
}
}
}
I know the target language is java, but I like pyhton and conversion will not be complicated.
You can define a 4-dimensional array dp where dp[i][a][b][c] is the maximum value that you can
get starting in position i when you already has a steps of length 2, b of length 5 and c of length
10. I use memoization to get a cleaner code.
import random
values = []
memo = {}
def dp(pos, n2, n5, n10):
state = (pos, n2, n5, n10)
if state in memo:
return memo[state]
res = values[pos]
if pos + 2 < len(values) and n2 > 0:
res = max(res, values[pos] + dp(pos + 2, n2 - 1, n5, n10))
if pos + 5 < len(values) and n5 > 0:
res = max(res, values[pos] + dp(pos + 5, n2, n5 - 1, n10))
if pos + 10 < len(values) and n10 > 0:
res = max(res, values[pos] + dp(pos + 10, n2, n5, n10 - 1))
memo[state] = res
return res
n2, n5, n10 = 5, 3, 2
values = [random.randint(1, 5) for _ in range(n2*2 + n5*5 + n10*10)]
print dp(0, n2, n5, n10)
Suspiciously like homework. Not tested:
import java.util.Arrays;
import java.util.Random;
public class Main {
private static Step[] steps = new Step[]{
new Step(2, 5),
new Step(5, 3),
new Step(10, 2)
};
private static final int[] pokestops = new int[calcLength(steps)];
private static int calcLength(Step[] steps) {
int total = 0;
for (Step step : steps) {
total += step.maxCount * step.size;
}
return total;
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
int[] initialCounts = new int[steps.length];
for (int i = 0; i < steps.length; i++) {
initialCounts[i] = steps[i].maxCount;
}
Counts counts = new Counts(initialCounts);
Tree base = new Tree(0, null, counts);
System.out.println(Tree.max.currentTotal);
}
static class Tree {
final int pos;
final Tree parent;
private final int currentTotal;
static Tree max = null;
Tree[] children = new Tree[steps.length*2];
public Tree(int pos, Tree parent, Counts counts) {
this.pos = pos;
this.parent = parent;
if (pos < 0 || pos >= pokestops.length || counts.exceeded()) {
currentTotal = -1;
} else {
int tmp = parent == null ? 0 : parent.currentTotal;
this.currentTotal = tmp + pokestops[pos];
if (max == null || max.currentTotal < currentTotal) max = this;
for (int i = 0; i < steps.length; i++) {
children[i] = new Tree(pos + steps[i].size, this, counts.decrement(i));
// uncomment to allow forward-back traversal:
//children[2*i] = new Tree(pos - steps[i].size, this, counts.decrement(i));
}
}
}
}
static class Counts {
int[] counts;
public Counts(int[] counts) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
this.counts = tmp;
}
public Counts decrement(int i) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
tmp[i] -= 1;
return new Counts(tmp);
}
public boolean exceeded() {
for (int count : counts) {
if (count < 0) return true;
}
return false;
}
}
static class Step {
int size;
int maxCount;
public Step(int size, int maxCount) {
this.size = size;
this.maxCount = maxCount;
}
}
}
There's a line you can uncomment to allow forward and back movement (I'm sure someone said in the comments that was allowed, but now I see in your post it says forward only...)
Why isn't this working? I am trying to check terms of the sequence a_j)=38^j +31 against values in the array. I am saying trying to get that if the specific term in the sequence is not divisible by any of these values in the array then print it.
public class SmallestPrime {
public static void main(String[] args) {
long m =0;
int [] b = {2,3,4,5,6,7,8};
for(int j=0;j<6;j++) {
m = (long) Math.pow(38,j) + 31;
if(m % b[j] !=0) {
System.out.println(m);
}
}
}
}
Your problem is that you are looping only once.
For example when j = 2 you only check if 1475 is divisible by b[2] which happens to be 4 and 1475 is not divisible by 4 thus printing your value.
You'll need to use a nested loop to achieve what you are trying to do.
Here is a bit of code to help out:
private static void smallestPrime() {
long m = 0;
int[] b = { 2, 3, 4, 5, 6, 7, 8 };
for (int j = 0; j <= 6; j++) {
m = (long) Math.pow(38, j) + 31;
boolean prime = true;
for (int i = 0; i <= 6; i++) {
if (m % b[i] == 0) {
prime = false;
break;
}
}
System.out.println(m + " : " + prime);
}
}
I am printing out a list of prime numbers in a program and storing it an array. Then I want to get the the prime number on a particular index instead of the total list..
import java.util.*;
public class Gauss {
static int n;
static int[] array;
public static void Input() {
Scanner input = new Scanner(System.in);
System.out.println("Enter N: ");
n = input.nextInt();
}
public static boolean isPrime(int num) {
boolean prime = true;
int limit = (int) Math.sqrt(num);
for (int i = 2; i <= limit; i++) {
if (num % i == 0) {
prime = false;
break;
}
}
return prime;
}
public static void Calc() {
Input();
array = new int[1000];
for (int i = 2; i < array.length; i++) {
array[i] = i;
}
ArrayList<Integer> list = new ArrayList<Integer>(array.length);
for (int c : array) {
list.add(c);
}
list.remove(0);
list.remove(0);
Collections.sort(list);
for (int k : list) {
if (isPrime(k)) {
System.out.println(k);
}
}
}
public static void main(String[] args) {
Calc();
}
}
To get the nth prime just use array[n-1]
You might find this answer useful to a similar question.
And you can get the nth prime numbers with
List<Integer> primes = findPrimes(0, n);
System.out.println( primes.get(i) );
** EDIT **
Here is the integral test program that I came up (modified since the last posted answer above) with benchmark tests and all. I know there are faster implementations, and some optimizations can still be made, but here are some algorithms to generate prime numbers :
public class PrimeTests {
public static void main(String... args) {
AbstractPrimeGenerator[] generators = new AbstractPrimeGenerator[] {
new DefaultPrimeGenerator(),
new AtkinSievePrimeGenerator(),
new SundaramSievePrimeGenerator()
};
int[] primes;
int[] old_primes = null;
double[] testAvg = new double[generators.length];
long ts, te;
double time;
DecimalFormat df = new DecimalFormat("0.0######################");
int max = 10000000;
int testCountLoop = 10;
int it = 0, ti;
while (it++ < testCountLoop) {
ti = 0;
for (AbstractPrimeGenerator g : generators) {
ti++;
System.out.println(it + "." + ti + ". Calculating " + max
+ " primes numbers from " + g.getName() + "...");
ts = System.nanoTime();
primes = g.findPrimes(max);
te = System.nanoTime();
time = (te - ts) * Math.pow(10, -9) * 1000;
df.setRoundingMode(RoundingMode.HALF_UP);
testAvg[ti - 1] += time;
System.out.println("Found " + primes.length
+ " prime numbers (in " + time + " ms, "
+ df.format(time / primes.length) + " ms per prime)");
// for (int prime : primes) {
// System.out.print(prime + "... ");
// }
// System.out.println();
if (old_primes != null) {
System.out.print("Validating primes.... ");
if (primes.length == old_primes.length) {
for (int i = 0; i < primes.length; i++) {
if (primes[i] != old_primes[i]) {
System.out.println("Prime number does not match : " + primes[i] + " != " + old_primes[i] + " at index " + i);
System.exit(-1);
}
}
} else {
System.out.println("ERROR!! No match in prime results");
System.exit(-1);
}
System.out.println("Ok!");
}
old_primes = primes;
}
System.out.println("................");
}
System.out.println("Results:");
ti = 0;
for (AbstractPrimeGenerator g : generators) {
time = (testAvg[ti++] / testCountLoop);
System.out.println(ti + ". Average time finding " + max
+ " primes numbers from " + g.getName() + " = " + time
+ " ms or " + df.format(time / old_primes.length)
+ " ms per prime");
}
System.out.println("Done!");
}
/**
* Base class for a prime number generator
*/
static abstract public class AbstractPrimeGenerator {
/**
* The name of the generator
*
* #return String
*/
abstract public String getName();
/**
* Returns all the prime numbers where (2 <= p <= max)
*
* #param max
* int the maximum value to test for a prime
* #return int[] an array of prime numbers
*/
abstract public int[] findPrimes(int max);
}
/**
* Default naive prime number generator. Based on the assumption that any
* prime n is not divisible by any other prime m < n (or more precisely m <=
* sqrt(n))
*/
static public class DefaultPrimeGenerator extends AbstractPrimeGenerator {
#Override
public String getName() {
return "Default generator";
}
#Override
public int[] findPrimes(int max) {
int[] primes = new int[max];
int found = 0;
boolean isPrime;
// initial prime
if (max > 2) {
primes[found++] = 2;
for (int x = 3; x <= max; x += 2) {
isPrime = true; // prove it's not prime
for (int i = 0; i < found; i++) {
isPrime = x % primes[i] != 0; // x is not prime if it is
// divisible by p[i]
if (!isPrime || primes[i] * primes[i] > x) {
break;
}
}
if (isPrime) {
primes[found++] = x; // add x to our prime numbers
}
}
}
return Arrays.copyOf(primes, found);
}
}
/**
* Sieve of Atkin prime number generator Implementation following the Sieve
* of Atkin to generate prime numbers
*
* #see http://en.wikipedia.org/wiki/Sieve_of_Atkin
*/
static public class AtkinSievePrimeGenerator extends AbstractPrimeGenerator {
#Override
public String getName() {
return "Sieve of Atkin generator";
}
#Override
public int[] findPrimes(int max) {
boolean[] isPrime = new boolean[max + 1];
double sqrt = Math.sqrt(max);
for (int x = 1; x <= sqrt; x++) {
for (int y = 1; y <= sqrt; y++) {
int n = 4 * x * x + y * y;
if (n <= max && (n % 12 == 1 || n % 12 == 5)) {
isPrime[n] = !isPrime[n];
}
n = 3 * x * x + y * y;
if (n <= max && (n % 12 == 7)) {
isPrime[n] = !isPrime[n];
}
n = 3 * x * x - y * y;
if (x > y && (n <= max) && (n % 12 == 11)) {
isPrime[n] = !isPrime[n];
}
}
}
for (int n = 5; n <= sqrt; n++) {
if (isPrime[n]) {
int s = n * n;
for (int k = s; k <= max; k += s) {
isPrime[k] = false;
}
}
}
int[] primes = new int[max];
int found = 0;
if (max > 2) {
primes[found++] = 2;
}
if (max > 3) {
primes[found++] = 3;
}
for (int n = 5; n <= max; n += 2) {
if (isPrime[n]) {
primes[found++] = n;
}
}
return Arrays.copyOf(primes, found);
}
}
/**
* Sieve of Sundaram prime number generator Implementation following the
* Sieve of Sundaram to generate prime numbers
*
* #see http://en.wikipedia.org/wiki/Sieve_of_Sundaram
*/
static public class SundaramSievePrimeGenerator extends
AbstractPrimeGenerator {
#Override
public String getName() {
return "Sieve of Sundaram generator";
}
#Override
public int[] findPrimes(int max) {
int n = max / 2;
boolean[] isPrime = new boolean[max];
Arrays.fill(isPrime, true);
for (int i = 1; i < n; i++) {
for (int j = i; j <= (n - i) / (2 * i + 1); j++) {
isPrime[i + j + 2 * i * j] = false;
}
}
int[] primes = new int[max];
int found = 0;
if (max > 2) {
primes[found++] = 2;
}
for (int i = 1; i < n; i++) {
if (isPrime[i]) {
primes[found++] = i * 2 + 1;
}
}
return Arrays.copyOf(primes, found);
}
}
}
On my machine, the result gives :
Results:
1. Average time finding 10000000 primes numbers from Default generator = 1108.7848961000002 ms or 0.0016684019448402676 ms per prime
2. Average time finding 10000000 primes numbers from Sieve of Atkin generator = 199.8792727 ms or 0.0003007607413114167 ms per prime
3. Average time finding 10000000 primes numbers from Sieve of Sundaram generator = 132.6467922 ms or 0.00019959522073372766 ms per prime
Using one of the class's method above (you don't need the actual base class and all, only the actual method), you can do :
public class PrimeTest2 {
static public int[] findPrimes(int max) {
int[] primes = new int[max];
int found = 0;
boolean isPrime;
// initial prime
if (max > 2) {
primes[found++] = 2;
for (int x = 3; x <= max; x += 2) {
isPrime = true; // prove it's not prime
for (int i = 0; i < found; i++) {
isPrime = x % primes[i] != 0; // x is not prime if it is
// divisible by p[i]
if (!isPrime || primes[i] * primes[i] > x) {
break;
}
}
if (isPrime) {
primes[found++] = x; // add x to our prime numbers
}
}
}
return Arrays.copyOf(primes, found);
}
public static void main(String... args) {
Scanner input = new Scanner(System.in);
int MAX_N = Integer.MAX_VALUE / 100;
int n = 0;
while (n <= 0 || n >= MAX_N) {
System.out.print("Enter N: ");
n = input.nextInt();
if (n <= 0) {
System.out.println("n must be greater than 0");
}
if (n >= MAX_N) {
System.out.println("n must be smaller than " + MAX_N);
}
}
int max = n * 100; // just find enough prime numbers....
int[] primes = findPrimes(max);
System.out.println("Number of prime numbers found from " + 0 + " to "
+ max + " = " + primes.length);
System.out.println("The " + n
+ (n == 1 ? "st" : n == 2 ? "nd" : n == 3 ? "rd" : "th")
+ " prime number is : " + primes[n - 1]);
}
}
Which will output (for example) :
Enter N: 10000
Number of prime numbers found from 0 to 1000000 = 78498
The 10000th prime number is : 104729
With that in hand, you pretty have all that is to say about finding the nth prime number. For larger numbers (beyond int's), you'll have to modify the "default generator's" un-optimized method to accept long or use other methodologies (i.e. other language and/or libraries)
Cheers!
The code you have is pretty much the way to go, and Roflcopter's answer for picking the number is correct, but there is one optimization you could do that would significantly increase the performance. Instead of dividing by all numbers less than or equal to the square root, divide only by PRIMES less than or equal to the square root. Any number not divisible by any prime you've found so far is also not divisible by any combination of same, which is the definition of a nonprime number (having a prime factorization other than 1*N)