How to check if three numbers are guessed? - java

Hi everyone on this community.
I'm a Java newbie. This is no homework, I'm really passionate about programming and I really want to learn more. I am stuck with one exercise.
Basically I have to create a simple lottery game.
The user has to input one number from 0 to 999, a three digit number.
If the guess is exactly the same, the prize is 10.000 $,
If the guess is about the same (digits guessed but not in order) the
prize is 3,000 $
If the guess is not really the same (digits guessed == 1) the prize
is 1,000 $.
Here's my code so far: I don't know how to deal with condition 2 and 3. Could I have some hint or comment?
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Please enter a three digit number");
int guess = input.nextInt();
//generate number:
int lottery = (int)(Math.random() * 1000);
if ((guess > 999) || (guess < 100)) {
System.out.println("The number is not a three digit number. The system will exit NOW.");
System.exit(1);
}
else {
// extract digits from lottery number
int lotteryDigit1 = lottery / 100;
int lotteryDigits = lottery % 100;
int lotteryDigit2 = lottery / 10;
int lotteryDigit3 = lottery % 10;
// extract digits from guessed number
int guessDigit1 = guess / 100;
int remainingDigits = guess % 100;
int guessDigit2 = guess / 10;
int guessDigit3 = guess % 10;
System.out.println("The lottery number is: " + lottery);
// check the guess:
if (guess == lottery) {
System.out.println("Exactly what the number was.");
}
else if {
// digits guessed, but not in exact order (eg. 332 = 233)
}
else if {
// only one digit is guessed (eg. 332 = 442)
}
}
}
}
Could I have some comment on the code? Is it readable or horribly written? I really don't have a clue since I am really just new to programming. Thanks.

With an array, you can count how many digits the real number and the guessed number contains, and perform comparisons with that. First make two arrays of length 10 (one slot for each digit), then increase each relevant "slot" by 1 for every digit in the real number. Do the same for the guess, and compare.
I'm recommending this approach because it will scale for guesses with 4, 5, or even 15 digits, whereas coding these checks by hand will quickly turn into a problem with lots of duplicate code.
If, for some reason, you're not allowed to use arrays, you could manually do the counting - it sounds tempting to check if each digit exists in the other set of digits, but you'll run into trouble with duplicate digits (consider 112, which, when compared to 122, would contain "all" the digits; just not in the right amounts!). This will lead to a lot of duplicate code.
It'd be something like this...
int matchingDigits = 0;
if ( guessDigit1 == lotteryDigit1 || guessDigit1 == lotteryDigit2 || guessDigit1 == lotteryDigit3) {
matchingDigits++;
}
if ( guessDigit2 == lotteryDigit1 || guessDigit2 == lotteryDigit2 || guessDigit2 == lotteryDigit3) {
matchingDigits++;
}
if ( guessDigit3 == lotteryDigit1 || guessDigit3 == lotteryDigit2 || guessDigit3 == lotteryDigit3) {
matchingDigits++;
}
As you can see, it's a lot of duplicate code, and it doesn't properly handle the 112 vs 122 case. To properly handle that, you'd have to split each check apart and cross out numbers you've already "used"... which is possible, but leads to really lengthy code.
Here's how you'd do it...
boolean digit1Used = false;
boolean digit2Used = false;
boolean digit3Used = false;
int matchingDigits = 0;
if ( guessDigit1 == lotteryDigit1) {
matchingDigits++;
digit1Used = true;
} else if ( guessDigit1 == lotteryDigit2) {
matchingDigits++;
digit2Used = true;
} else if ( guessDigit1 == lotteryDigit3) {
matchingDigits++;
digit3Used = true;
}
if ( guessDigit2 == lotteryDigit1 && !digit1Used) {
matchingDigits++;
digit1Used = true;
} else if ( guessDigit2 == lotteryDigit2 && !digit2Used) {
matchingDigits++;
digit2Used = true;
} else if ( guessDigit2 == lotteryDigit3 && !digit3Used) {
matchingDigits++;
digit3Used = true;
}
if ( guessDigit3 == lotteryDigit1 && !digit1Used) {
matchingDigits++;
} else if ( guessDigit3 == lotteryDigit2 && !digit2Used) {
matchingDigits++;
} else if ( guessDigit3 == lotteryDigit3 && !digit3Used) {
matchingDigits++;
}
Note that I left out the checks for digitUsed in the first checks and the sets for digitUsed for the third set of checks - because we're not going to use them anymore. As you can see, it's really long AND it's basically faking arrays. Hence why it's not recommended to do so.
Imagine that solution for a 15 digit array! It'd be (N + 1 + (N*3*N) + N + N) lines long - 15 for digitUsed declarations, 1 for the matching digits, 15 digit comparisons of 3 lines for 15 digits, plus 15 closing braces and 15 blank lines in between - a total of 721 lines! That's why you should use arrays for this, and not checking it by hand.
If you've learned about methods already, I suggest you put the digit matcher in a separate function; that way you can keep your digit matching logic and the result-to-prize matching logic somewhat separated.

It’s not going to be nice code. For the second bullet, guessing all the digits but not in the right order, you may write a class like:
public class ThreeDigitNumber {
int digit1;
int digit2;
int digit3;
public ThreeDigitNumber(int number) {
digit1 = number / 100;
int remainingDigits = number % 100;
digit2 = remainingDigits / 10;
digit3 = remainingDigits % 10;
}
public int getDigit1() {
return digit1;
}
public int getDigit2() {
return digit2;
}
public int getDigit3() {
return digit3;
}
boolean isDigitGuessed(int digit) {
if (digit == digit1) {
// allow digit to be guessed only once, so set to a non-digit value when returning true
digit1 = -1;
return true;
}
if (digit == digit2) {
digit2 = -1;
return true;
}
if (digit == digit3) {
digit3 = -1;
return true;
}
return false;
}
}
Now you can test whether all digits have been guessed:
ThreeDigitNumber lotteryTdn = new ThreeDigitNumber(lottery);
ThreeDigitNumber guessTdn = new ThreeDigitNumber(guess);
boolean allDigitsGuessed = false;
if (lotteryTdn.isDigitGuessed(guessTdn.getDigit1())) {
if (lotteryTdn.isDigitGuessed(guessTdn.getDigit2())) {
if (lotteryTdn.isDigitGuessed(guessTdn.getDigit3())) {
allDigitsGuessed = true; // 3000 $ won
}
}
}

Related

Checking if two three digit numbers have any of there digits that are the same in Java

I'm trying to write a program in Java that checks if at least one digit of one of the numbers, matches one digit of the other number. For example, if the two numbers are 238 and 345 it should return a certain string. So far this is what I have, but it only works properly for the right most digit:
if ((computerGuess/10) == (userGuess/10) || (computerGuess%10) == (userGuess%10) || (computerGuess%10) == (userGuess/10) || (userGuess%10) == (computerGuess/10)) {
System.out.println("You have won S1000");
break;
}
One clean solution would be to convert each number into a set of digits, and then check for overlap:
public Set<Integer> getDigits (int input) {
Set<Integer> digits = new HashSet<>();
while (input > 0) {
digits.add(input % 10);
input /= 10;
}
return digits;
}
int computerGuess = 238;
int userGuess = 345;
Set<Integer> computerDigits = getDigits(computerGuess);
Set<Integer> userDigits = getDigits(userGuess);
computerDigits.retainAll(userDigits);
if (computerDigits.size() > 0) {
System.out.println("There is an overlap: " + computerDigits.toString());
}
The reason is always you are using "10", either '/10' or '%10'.
The best solution would convert them into string, and check for sub-string.
String computerGuessStr = new String (computerGuess);
String userGuess = new String (userGuess);
for (int i=0;i<computerGuessStr.length();i++){
char computerChar = computerGuessStr.getCharAt(i);
if(userGuess.contains(computerChar)){
//do something
} else {
//do something
}
Something like this?
int computerNumber = 543;
int guess = 238;
List<Integer> numbers = Stream.of(10, 100, 1000).map(n -> guess % n).collect(Collectors.toList());
Stream.of(10,100,1000).map(n -> computerNumber % n).anyMatch(num -> numbers.contains(num));
Another option is the plain old loop. This will work with any length number.
boolean digitMatch(int num1, int num2)
{
for ( ; num1 > 0; num1 /= 10) {
// Get a digit from num1
int digit1 = num1 % 10;
for (int n2 = num2; n2 > 0; n2 /= 10) {
// Get a digit from num2
int digit2 = n2 % 10;
// compare
if (digit1 == digit2) return true;
}
}
return false;
}
Note: you need to add a little extra code for negative numbers.

multiple conditions else / if statement somehow wrong?

I'm doing a hackernet challenge where n is an int input. The conditions are:
If n is odd, print Weird
If n is even and in the inclusive range of 2 to 5, print Not Weird
If n is even and in the inclusive range of 6 to 20, print Weird
If n is even and greater than 20, print Not Weird.
Im sure the code makes logic and dont think theres syntax. It gives the correct responses and hackernet still says its incorrect so ive come here to see if anyone can see what the problem is
public static void main(String[] args)
{
int N = scanner.nextInt();
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
if (N % 2 != 0 || N % 2 == 0 && N >= 6 && N <= 20)
{
System.out.print("weird");
}
else
{
System.out.print("not weird");
}
}
The problem is the logic in your else condition, which would also catch values of N which are less than 2. Try this version:
if (N % 2 != 0)
{
System.out.print("weird");
}
else if (N >= 2 && N <= 5 || N > 20)
{
System.out.print("not weird");
}
else if (N >= 6 && N <= 20)
{
System.out.print("weird");
}
else
{
// NOTE: if the code still fails, remove this else condition
System.out.print("unexpected value of N");
}
Note: To get your code to pass the Hackernet task, you might have to completely remove the else condition. I added it for completeness, but Hackernet might test N=1 to see if nothing gets printed.
Read this condition :
if (N % 2 != 0 || N % 2 == 0 && N >= 6 && N <= 20)
as
if (N % 2 != 0 || (N % 2 == 0 && N >= 6 && N <= 20))
Then see how operator precedence changes the behaviour and yield desired results.
Check the following one
public static void main(String[] args)
{
int N = scanner.nextInt();
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
if(N%2!=0) {
System.out.print("weird");
}else if(N>=2 && N<=5) {
System.out.print("not weird");
}else if(N>=6 && N<=20) {
System.out.print("weird");
}else if(N>20) {
System.out.print("not weird");
}
}
For the technical part: start by reading about
precedence of java operators and then make your code easier to read.
Pushing that many conditions into a single if is not helpful. You see it yourself: you think the code is correct, but probably it isn't. And now you look to other people to explain your overly complex code back to you. And of course, all the other answers do all that for you ... but beyond that:
The "real" answer here is: learn how to test your code.
Instead of having a main that somehow asks for a number, and then makes decisions, write a method boolean isWeird() that takes a number and returns true/false according to your requirements.
And then simply test that method with all reasonable cases. And then check if that result is as expected.
Using JUnit, you could write something like
assertThat(isWeird(1), true);
assertThat(isWeird(21), true);
assertThat(isWeird(22), true);
...
Ideally, you write such tests before you implement that method. And then you implement all the conditions, and any check that fails tells you that you got something wrong.
I feel, In the if (N % 2 != 0 || N % 2 == 0 && N >= 6 && N <= 20) condition, you are verifiying the odd and even values at same time using && and || operator. Can you modify the condition into like this if (N % 2 != 0 || (N % 2 == 0 && N >= 6 && N <= 20)) and check? If N is odd weird will be printed or if N is even and it falls under the 6 and 20 inclusive, weird will be printed.
You already have a good few answers here but if you think logically about what you actually need, you can break it down easier.
It looks like the only "Not Weird" print out is 2, 4 and even numbers > 20
So an example could be something like:
if (n % 2 == 0) {
if ((n >= 2 && n <= 5) || (n > 20)) {
return "Not Weird";
}
}
return "Weird";
You can try this
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
int n = scanner.nextInt();
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
if (n % 2 == 1 || (n >= 6 && n <= 20)) {
System.out.println("Weird");
} else {
System.out.println("Not Weird");
}
scanner.close();
}

Java, nested if, if/else statements to add words

I'm doing an assignment where I need to do a multitude of things that require nested if/else. Print the first 50 Fibonacci numbers, but:
if the number is a multiple of 3 - print "Cheese"
if the number is a multiple of 5 - print "Cake"
if the number is a multiple of 7 - print "Factory"
if the number is a multiple of 3 & 5 - print "CheeseCake"
if the number is a multiple of 3 & 7 - print "CheeseFactory"
if the number is a multiple of 5 & 7 - print "CakeFactory"
if the number is a multiple of 3 & 5 & 7 - print "CheeseCakeFactory"
if the number is a multiple of 2 - print "Blah"
At this point I'm repeating the conditions, and I'm sure there's a cleaner way to do it:
package Assignment1;
public class CheeseCakeFactory_163003984 {
public static void main(String[] args) {
long numberOne = 0;
long numberTwo = 1;
long sum = 0;
int counter = 0;
String word1 = "Cheese";
String word2 = "Cake";
String word3 = "Factory";
while (counter <= 50) {
sum = numberOne + numberTwo;
numberOne = numberTwo;
numberTwo = sum;
counter++;
if (sum % 3 == 0) {
System.out.print(word1 + ", ");
} else if (sum % 5 == 0) {
if (sum % 3 == 0) {
System.out.print(word1 + word2 + ", ");
} else if (sum % 3 == 0) {
if (sum % 5 == 0) {
if (sum % 7 == 0) {
System.out.print(word1 + word2 + word3 + ", ");
}
}
if (sum % 7 == 0) {
System.out.print(word1 + word2 + word3 + ", ");
} else if (sum % 2 == 0) {
System.out.print("Blah, ");
} else {
System.out.print(sum);
if (counter % 10 == 0) {
System.out.print("\n");
} else {
System.out.print(", ");
}
}
}
}
}
}
}
There is a good reason behind the names "CheeseFactory", "CakeFactory", and "CheeseCakeFactory". It is so that you do not have to repeat your statements.
Let us say you have an array with the first 50 fibonacci-numbers already called numbers.
for(int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + ": ");
if(numbers[i] % 3 == 0) {
System.out.print("Cheese");
}
if(numbers[i] % 5 == 0) {
System.out.print("Cake");
}
if(numbers[i] % 7 == 0) {
System.out.print("Factory");
}
System.out.println(""); //start a new line
}
I have not covered what will happen if it's a multiple of two, since it is ambiguous. What if it's both a multiple of two and three?
The idea here is that you want to build out the statements one at a time instead of all at once. Here I'm going to leverage something known as StringBuilder which will allow us to neatly and concisely build out the string we want.
If we know the sum to be divisible by 3, we add the word we want to the appender.
if (sum % 3 == 0) {
builder.append(word1);
}
If we know the sum to be divisible by 3 and by 5, we add the words we want to the appender.
if (sum % 3 == 0) {
builder.append(word1);
}
if(sum % 5 == 0) {
builder.append(word2);
}
Nothing special needs to happen in terms of other logic; simple if conditions will get you the result you need. If it's not true, the if block isn't executed.
I leave the other forms (including the even number form and actually also printing out the number - hint: if you haven't printed any other words, you may want to print the number) as an exercise for the reader.
I tried your code and it was printing all the time the word Cheese.
I refactored it a little bit to fulfill your requirements ( I hope I understood them :) )
public static void main(String[] args) {
long numberOne = 0;
long numberTwo = 1;
long sum = 0;
int counter = 0;
String word1 = "Cheese";
String word2 = "Cake";
String word3 = "Factory";
String word4 = "Blah";
while (counter <= 50) {
sum = numberOne + numberTwo;
numberOne = numberTwo;
numberTwo = sum;
counter++;
StringBuilder sb = new StringBuilder();
if (sum % 2 == 0) {
sb.append(word4);
} else {
if (sum % 3 == 0) sb.append(word1);
if (sum % 5 == 0) sb.append(word2);
if (sum % 7 == 0) sb.append(word3);
}
if (sum % 2 == 0 || sum % 3 == 0 || sum % 5 == 0 || sum % 7 == 0) {
sb.append(",");
System.out.println(String.format("%s %s", sum, sb.toString()));
}
}
}
You can then refactor it extracting it to a word factory method and so on
This will print
2 Blah,
3 Cheese,
5 Cake,
8 Blah,
21 CheeseFactory,
34 Blah,
55 Cake,
144 Blah,
610 Blah,
987 CheeseFactory,
2584 Blah,
6765 CheeseCake,
10946 Blah,
46368 Blah,
75025 Cake,
196418 Blah,
317811 Cheese,
832040 Blah,
2178309 CheeseFactory,
3524578 Blah,
9227465 Cake,
14930352 Blah,
63245986 Blah,
102334155 CheeseCakeFactory,
267914296 Blah,
701408733 Cheese,
1134903170 Blah,
4807526976 Blah,
12586269025 Cake,
20365011074 Blah,
32951280099 Cheese,
Instead of nesting for each possible condition, you can check for multiple conditions in the same if statement. For example, to check if a number is a multiple of 3 and 5, you can simply write:
if(sum % 3 == 0 && sum % 5 == 0) // check if this number is a multiple of 3 and 5
System.out.print("cheesecake");
Using if statements like the example I just gave, you could do all of those things without nesting ifs and just use else ifs or additional, separate ifs.
If the way the statement is written is confusing, you can use parentheses to group the statements together for better readability:
if((sum % 3 == 0) && (sum % 5 == 0))
System.out.print("cheesecake");
Regardless of which way you write it, the result will be the same, though.

conditional statement using data types

Just curious is there a way to set an if statement condition that checks if value is equal to a data type. Like if (x=int) kind of thing.
I am suppose to create a program that checks for prime numbers but i do not know how to do it other than divide the number(entered by user) by the building block number (2-9(excluding 1 because all numbers are divisible ...)) and if all of them return a float then the number is prime.
I also think this way is inefficient but again i do not have any other clue how i would do it. So if possible some ideas would work too.
This is the structure of the code:
package innocence;
import java.util.*;
class GS1
{
public static void main(String[]args)
{
Scanner input = new Scanner(System.in);
double num;
System.out.println("Enter a number to check if it is prime or not");
num=input.nextDouble();
if((num % 2 == float) || (num % 3== float) || (num % 4 == float)|| (num % 5 == float)|| (num % 6 == float)|| (num % 7 == float)|| (num % 8 == float)|| (num % 9 == float) ) // float returns an error but i want to do it so it checks if it is a float or not
{
System.out.println("The number you haver entered," + num + "is a prime number");
}
else
{
System.out.println("The number entered is not prime");
}
}
}
You can check if the number is equal to the number rounded to the nearest integer:
public static boolean isInteger(double n) {
return n == Math.round(n);
}
if(!isInteger(num % 2) && !isInteger(num % 3) && !isInteger(num % 4) &&
!isInteger(num % 5) && !isInteger(num % 6) && !isInteger(num % 7) &&
!isInteger(num % 8) && !isInteger(num % 9) )
{
System.out.println("The number you haver entered," + num + "is a prime number");
}
else
{
System.out.println("The number entered is not prime");
}
If course this doesn't really check if the number is prime or not; it only checks whether the number is divisible by the integers 2 to 9.

What would be the fastest method to test for primality in Java?

I am trying to find the fastest way to check whether a given number is prime or not (in Java). Below are several primality testing methods I came up with. Is there any better way than the second implementation(isPrime2)?
public class Prime {
public static boolean isPrime1(int n) {
if (n <= 1) {
return false;
}
if (n == 2) {
return true;
}
for (int i = 2; i <= Math.sqrt(n) + 1; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
public static boolean isPrime2(int n) {
if (n <= 1) {
return false;
}
if (n == 2) {
return true;
}
if (n % 2 == 0) {
return false;
}
for (int i = 3; i <= Math.sqrt(n) + 1; i = i + 2) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
public class PrimeTest {
public PrimeTest() {
}
#Test
public void testIsPrime() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Prime prime = new Prime();
TreeMap<Long, String> methodMap = new TreeMap<Long, String>();
for (Method method : Prime.class.getDeclaredMethods()) {
long startTime = System.currentTimeMillis();
int primeCount = 0;
for (int i = 0; i < 1000000; i++) {
if ((Boolean) method.invoke(prime, i)) {
primeCount++;
}
}
long endTime = System.currentTimeMillis();
Assert.assertEquals(method.getName() + " failed ", 78498, primeCount);
methodMap.put(endTime - startTime, method.getName());
}
for (Entry<Long, String> entry : methodMap.entrySet()) {
System.out.println(entry.getValue() + " " + entry.getKey() + " Milli seconds ");
}
}
}
Here's another way:
boolean isPrime(long n) {
if(n < 2) return false;
if(n == 2 || n == 3) return true;
if(n%2 == 0 || n%3 == 0) return false;
long sqrtN = (long)Math.sqrt(n)+1;
for(long i = 6L; i <= sqrtN; i += 6) {
if(n%(i-1) == 0 || n%(i+1) == 0) return false;
}
return true;
}
and BigInteger's isProbablePrime(...) is valid for all 32 bit int's.
EDIT
Note that isProbablePrime(certainty) does not always produce the correct answer. When the certainty is on the low side, it produces false positives, as #dimo414 mentioned in the comments.
Unfortunately, I could not find the source that claimed isProbablePrime(certainty) is valid for all (32-bit) int's (given enough certainty!).
So I performed a couple of tests. I created a BitSet of size Integer.MAX_VALUE/2 representing all uneven numbers and used a prime sieve to find all primes in the range 1..Integer.MAX_VALUE. I then looped from i=1..Integer.MAX_VALUE to test that every new BigInteger(String.valueOf(i)).isProbablePrime(certainty) == isPrime(i).
For certainty 5 and 10, isProbablePrime(...) produced false positives along the line. But with isProbablePrime(15), no test failed.
Here's my test rig:
import java.math.BigInteger;
import java.util.BitSet;
public class Main {
static BitSet primes;
static boolean isPrime(int p) {
return p > 0 && (p == 2 || (p%2 != 0 && primes.get(p/2)));
}
static void generatePrimesUpTo(int n) {
primes = new BitSet(n/2);
for(int i = 0; i < primes.size(); i++) {
primes.set(i, true);
}
primes.set(0, false);
int stop = (int)Math.sqrt(n) + 1;
int percentageDone = 0, previousPercentageDone = 0;
System.out.println("generating primes...");
long start = System.currentTimeMillis();
for(int i = 0; i <= stop; i++) {
previousPercentageDone = percentageDone;
percentageDone = (int)((i + 1.0) / (stop / 100.0));
if(percentageDone <= 100 && percentageDone != previousPercentageDone) {
System.out.println(percentageDone + "%");
}
if(primes.get(i)) {
int number = (i * 2) + 1;
for(int p = number * 2; p < n; p += number) {
if(p < 0) break; // overflow
if(p%2 == 0) continue;
primes.set(p/2, false);
}
}
}
long elapsed = System.currentTimeMillis() - start;
System.out.println("finished generating primes ~" + (elapsed/1000) + " seconds");
}
private static void test(final int certainty, final int n) {
int percentageDone = 0, previousPercentageDone = 0;
long start = System.currentTimeMillis();
System.out.println("testing isProbablePrime(" + certainty + ") from 1 to " + n);
for(int i = 1; i < n; i++) {
previousPercentageDone = percentageDone;
percentageDone = (int)((i + 1.0) / (n / 100.0));
if(percentageDone <= 100 && percentageDone != previousPercentageDone) {
System.out.println(percentageDone + "%");
}
BigInteger bigInt = new BigInteger(String.valueOf(i));
boolean bigIntSays = bigInt.isProbablePrime(certainty);
if(isPrime(i) != bigIntSays) {
System.out.println("ERROR: isProbablePrime(" + certainty + ") returns "
+ bigIntSays + " for i=" + i + " while it " + (isPrime(i) ? "is" : "isn't" ) +
" a prime");
return;
}
}
long elapsed = System.currentTimeMillis() - start;
System.out.println("finished testing in ~" + ((elapsed/1000)/60) +
" minutes, no false positive or false negative found for isProbablePrime(" + certainty + ")");
}
public static void main(String[] args) {
int certainty = Integer.parseInt(args[0]);
int n = Integer.MAX_VALUE;
generatePrimesUpTo(n);
test(certainty, n);
}
}
which I ran by doing:
java -Xmx1024m -cp . Main 15
The generating of the primes took ~30 sec on my machine. And the actual test of all i in 1..Integer.MAX_VALUE took around 2 hours and 15 minutes.
This is the most elegant way:
public static boolean isPrime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
Java 1.4+. No imports needed.
So short. So beautiful.
You took the first step in eliminating all multiples of 2.
However, why did you stop there? you could have eliminated all multiples of 3 except for 3, all multiples of 5 except for 5, etc.
When you follow this reasoning to its conclusion, you get the Sieve of Eratosthenes.
Take a look at the AKS primality test (and its various optimizations). It is a deterministic primality test that runs in polynomial time.
There is an implementation of the algorithm in Java from the University of Tuebingen (Germany) here
i think this method is best. at least for me-
public static boolean isPrime(int num)
{
for (int i = 2; i<= num/i; i++)
{
if (num % i == 0)
{
return false;
}
}
return num > 1;
}
Your algorithm will work well for reasonably small numbers. For big numbers, advanced algorithms should be used (based for example on elliptic curves). Another idea will be to use some "pseuso-primes" test. These will test quickly that a number is a prime, but they aren't 100% accurate. However, they can help you rule out some numbers quicker than with your algorithm.
Finally, although the compiler will probably optimise this for you, you should write:
int max = (int) (Math.sqrt(n) + 1);
for (int i = 3; i <= max; i = i + 2) {
}
A fast test due to Jaeschke (1993) is a deterministic version of the Miller-Rabin test, which has no false positives below 4,759,123,141 and hence can be applied to Java ints.
// Given a positive number n, find the largest number m such
// that 2^m divides n.
private static int val2(int n) {
int m = 0;
if ((n&0xffff) == 0) {
n >>= 16;
m += 16;
}
if ((n&0xff) == 0) {
n >>= 8;
m += 8;
}
if ((n&0xf) == 0) {
n >>= 4;
m += 4;
}
if ((n&0x3) == 0) {
n >>= 2;
m += 2;
}
if (n > 1) {
m++;
}
return m;
}
// For convenience, handle modular exponentiation via BigInteger.
private static int modPow(int base, int exponent, int m) {
BigInteger bigB = BigInteger.valueOf(base);
BigInteger bigE = BigInteger.valueOf(exponent);
BigInteger bigM = BigInteger.valueOf(m);
BigInteger bigR = bigB.modPow(bigE, bigM);
return bigR.intValue();
}
// Basic implementation.
private static boolean isStrongProbablePrime(int n, int base) {
int s = val2(n-1);
int d = modPow(base, n>>s, n);
if (d == 1) {
return true;
}
for (int i = 1; i < s; i++) {
if (d+1 == n) {
return true;
}
d = d*d % n;
}
return d+1 == n;
}
public static boolean isPrime(int n) {
if ((n&1) == 0) {
return n == 2;
}
if (n < 9) {
return n > 1;
}
return isStrongProbablePrime(n, 2) && isStrongProbablePrime(n, 7) && isStrongProbablePrime(n, 61);
}
This doesn't work for long variables, but a different test does: the BPSW test has no counterexamples up to 2^64. This basically consists of a 2-strong probable prime test like above followed by a strong Lucas test which is a bit more complicated but not fundamentally different.
Both of these tests are vastly faster than any kind of trial division.
If you are just trying to find if a number is prime or not it's good enough, but if you're trying to find all primes from 0 to n a better option will be the Sieve of Eratosthenes
But it will depend on limitations of java on array sizes etc.
There are of course hundreds of primality tests, all with various advantages and disadvantages based on size of number, special forms, factor size, etc.
However, in java I find the most useful one to be this:
BigInteger.valueOf(long/int num).isProbablePrime(int certainty);
Its already implemented, and is quite fast (I find it takes ~6 seconds for a 1000x1000 matrix filled with longs 0–2^64 and a certainty of 15) and probably better optimized than anything we mortals could come up with.
It uses a version of the Baillie–PSW primality test, which has no know counterexamples. (though it might use a slightly weaker version of the test, which may err sometimes. maybe)
What you have written is what most common programmers do and which should be sufficient most of the time.
However, if you are after the "best scientific algorithm" there are many variations (with varying levels of certainty) documented http://en.wikipedia.org/wiki/Prime_number.
For example, if you have a 70 digit number JVM's physical limitations can prevent your code from running in which case you can use "Sieves" etc.
Again, like I said if this was a programming question or a general question of usage in software your code should be perfect :)
Dependent on the length of the number you need to test you could precompute a list of prime numbers for small values (n < 10^6), which is used first, if the asked number is within this range. This is of course the fastest way.
Like mentioned in other answers the Sieve of Eratosthenes is the preferred method to generate such a precomputed list.
If your numbers are larger than this, you can use the primality test of Rabin.
Rabin primality test
Algorithm Efficiency : O( n^(1/2)) Algorithm
Note: This sample code below contains count variables and calls to a print function for the purposes of printing the results :
import java.util.*;
class Primality{
private static void printStats(int count, int n, boolean isPrime) {
System.err.println( "Performed " + count + " checks, determined " + n
+ ( (isPrime) ? " is PRIME." : " is NOT PRIME." ) );
}
/**
* Improved O( n^(1/2)) ) Algorithm
* Checks if n is divisible by 2 or any odd number from 3 to sqrt(n).
* The only way to improve on this is to check if n is divisible by
* all KNOWN PRIMES from 2 to sqrt(n).
*
* #param n An integer to be checked for primality.
* #return true if n is prime, false if n is not prime.
**/
public static boolean primeBest(int n){
int count = 0;
// check lower boundaries on primality
if( n == 2 ){
printStats(++count, n, true);
return true;
} // 1 is not prime, even numbers > 2 are not prime
else if( n == 1 || (n & 1) == 0){
printStats(++count, n, false);
return false;
}
double sqrtN = Math.sqrt(n);
// Check for primality using odd numbers from 3 to sqrt(n)
for(int i = 3; i <= sqrtN; i += 2){
count++;
// n is not prime if it is evenly divisible by some 'i' in this range
if( n % i == 0 ){
printStats(++count, n, false);
return false;
}
}
// n is prime
printStats(++count, n, true);
return true;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while(scan.hasNext()) {
int n = scan.nextInt();
primeBest(n);
System.out.println();
}
scan.close();
}
}
When the prime number 2147483647 is entered, it produces the following output:
Performed 23170 checks, determined 2147483647 is PRIME.
tested in a Intel Atom # 1.60GHz, 2GB RAM, 32-bit Operating System
test result:
largest prime number below Long.MAX_VALUE=9223372036854775807 is 9223372036854775783
elapsed time is 171499 milliseconds or 2 minutes and 51 seconds
public class PrimalityTest
{
public static void main(String[] args)
{
long current_local_time = System.currentTimeMillis();
long long_number = 9223372036854775783L;
long long_a;
long long_b;
if (long_number < 2)
{
System.out.println(long_number + " is not a prime number");
}
else if (long_number < 4)
{
System.out.println(long_number + " is a prime number");
}
else if (long_number % 2 == 0)
{
System.out.println(long_number + " is not a prime number and is divisible by 2");
}
else
{
long_a = (long) (Math.ceil(Math.sqrt(long_number)));
terminate_loop:
{
for (long_b = 3; long_b <= long_a; long_b += 2)
{
if (long_number % long_b == 0)
{
System.out.println(long_number + " is not a prime number and is divisible by " + long_b);
break terminate_loop;
}
}
System.out.println(long_number + " is a prime number");
}
}
System.out.println("elapsed time: " + (System.currentTimeMillis() - current_local_time) + " millisecond/s");
}
}
First and foremost, primes start from 2. 2 and 3 are primes. Prime should not be dividable by 2 or 3. The rest of the primes are in the form of 6k-1 and 6k+1. Note that you should check the numbers up to SQRT(input). This approach is very efficient. I hope it helps.
public class Prime {
public static void main(String[] args) {
System.out.format("%d is prime: %s.\n", 199, isPrime(199)); // Prime
System.out.format("%d is prime: %s.\n", 198, isPrime(198)); // Not prime
System.out.format("%d is prime: %s.\n", 104729, isPrime(104729)); // Prime
System.out.format("%d is prime: %s.\n", 104727, isPrime(982443529)); // Prime
}
/**
* Tells if a number is prime or not.
*
* #param input the input
* #return If the input is prime or not
*/
private boolean isPrime(long input) {
if (input <= 1) return false; // Primes start from 2
if (input <= 3) return true; // 2 and 3 are primes
if (input % 2 == 0 || input % 3 == 0) return false; // Not prime if dividable by 2 or 3
// The rest of the primes are in the shape of 6k-1 and 6k+1
for (long i = 5; i <= Math.sqrt(input); i += 6) if (input % i == 0 || input % (i + 2) == 0) return false;
return true;
}
}
In general, all primes greater than some Primorial integer C is of the form Ck+i for i < C where i and k are integers and i represents the numbers that are coprime to C
Here is an example with C=30, it should work faster than Bart Kiers answer for C=6 and you can improve it by computing C=210
boolean isPrime(long n) {
if(n < 2){
return false;
}
if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13 || n == 17 || n == 19 || n == 23 || n == 29){
return true;
}
long sqrtN = (long) Math.sqrt(n) + 1;
int[] mods = {1, 7, 11, 13, 17, 19, 23, 29};
for (long i = 30L; i <= sqrtN; i += 30) {
for (int mod : mods) {
if(n % (i + mod) == 0){
return false;
}
}
}
return true;
}

Categories