Java: Scanner Issue? - java

I have a pretty simple little program here that reads in an integer, encrypts it, and then decrypts it, but for whatever reason sometimes it gives me the right answers while other times it doesn't. Code:
public class Cipher {
public static void main(String[] args) {
System.out.println("Welcome to Caesar Cipher Demo:");
Scanner scanner = new Scanner(System.in);
CaesarCipher cipher = new CaesarCipher();
int a, n, x, y;
int i = 1;
while(true) {
System.out.println("====== General Test ======");
System.out.println("Please specify the following numbers:");
System.out.printf("n - ");
n = scanner.nextInt();
scanner.nextLine();
System.out.print("a - ");
a = scanner.nextInt();
scanner.nextLine();
cipher.setN(n);
cipher.setA(a);
//scanner.nextLine();
System.out.printf("plaintext value from [0 - %d] - ", (n-1));
x = scanner.nextInt();
y = cipher.encrypt(x);
System.out.printf("ciphertext value - %d%n", y);
x = cipher.decrypt(y);
System.out.printf("back to plaintext value - %d%n", x);
scanner.nextLine();
System.out.printf("Continue? (1 for yes, 0 for no) - ");
i = scanner.nextInt();
scanner.nextLine();
if(i == 0) break;
}
while(true){
System.out.println("====== Text Message Test ======");
cipher.setN(26);
System.out.printf("key [0-25] - ");
a = scanner.nextInt();
scanner.nextLine();
while(a < 0 || a > 25){
System.out.printf("key must be from [0-25] - ");
a = scanner.nextInt();
scanner.nextLine();
}
cipher.setA(a);
System.out.printf("plaintext (use English alphabet)- ");
String plaintext = scanner.nextLine();
plaintext = plaintext.toLowerCase();
String ciphertext = cipher.encrypt(plaintext);
System.out.printf("ciphertext - %s%n", ciphertext);
plaintext = cipher.decrypt(ciphertext);
System.out.printf("plaintext - %s%n", plaintext);
System.out.printf("Continue? (1 for yes, 0 for no) - ");
i = scanner.nextInt();
scanner.nextLine();
if(i == 0) break;
}
}
}
class CaesarCipher {
private int a;
private int n;
private char[] letters;
public CaesarCipher() {
this.letters = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
}
public int getA() {
return a;
}
public int getN() {
return n;
}
public void setA(int a) {
this.a = a;
}
public void setN(int n) {
this.n = n;
}
public int encrypt(int x){
if (x >= n){
System.out.println("X must be less than "+n+"-1");
}
return (a+x) % n;
}
public int inverseOf(int e){
return (n - e %n);
}
public int decrypt(int y){
if (y >= n) {
System.out.println("N must be "+ n +"-1");
}
return (inverseOf(a) + y % n);
}
public String encrypt(String plaintext){
char[] ciphertext= new char[plaintext.length()];
for (int j = 0; j<plaintext.length();j++) {
char c = plaintext.charAt(j);
int index = c- 'a';
if (index < 0 || index > 25) {
System.out.println("Illegal character!");
System.exit(1);
}
index = encrypt(index);
ciphertext[j] = letters[index];
}
return new String(ciphertext);
}
public String decrypt(String ciphertext){
char[] plaintext = new char[ciphertext.length()];
for (int j = 0; j < ciphertext.length();j++) {
char c = ciphertext.charAt(j);
int index = c- 'a';
index = decrypt(index);
plaintext[j] = letters[index];
}
return new String(plaintext);
}
}
Output I'm getting:
Welcome to Caesar Cipher Demo:
====== General Test ======
Please specify the following numbers:
n - 54
a - 44
plaintext value from [0 - 53] - 15
ciphertext value - 5
back to plaintext value - 15
Continue? (1 for yes, 0 for no) - 1
====== General Test ======
Please specify the following numbers:
n - 78
a - 55
plaintext value from [0 - 77] - 7
ciphertext value - 62
back to plaintext value - 85
Continue? (1 for yes, 0 for no) - 1
====== General Test ======
Please specify the following numbers:
n - 55
a - 54
plaintext value from [0 - 54] - 6
ciphertext value - 5
back to plaintext value - 6
Continue? (1 for yes, 0 for no) - 1
====== General Test ======
Please specify the following numbers:
n - 632
a - 43
plaintext value from [0 - 631] - 66
ciphertext value - 109
back to plaintext value - 698
Continue? (1 for yes, 0 for no) - 1
====== General Test ======
Please specify the following numbers:
n - 25
a - 3
plaintext value from [0 - 24] - 5
ciphertext value - 8
back to plaintext value - 30
Continue? (1 for yes, 0 for no) -
As you can see, sometimes it returns the right back to plaintext value and sometimes it adds n and the plaintext value from [0 - n-1]. I believe this is due to a scanner issue, but can't figure out where exactly the problem is coming from. I was hoping another set of eyes could see something I can't. Thanks for your time, I really do appreciate it!

I'm not sure what you are trying to do in your decrypt function, especially the method inverseOf. But it's not correct. What you want in the decrypt function is just to subtract the key from the encrypted value (and then keep it in the correct range using the modulo operator, and and extra check that's it not negative because the modulo operator in itself is not enough to keep the value inside the bottom end of the range)
public int decrypt(int y) {
if (y >= n) {
System.out.println("N must be " + n + "-1");
}
if (y < a)
return (y - a + n) % n;
else
return (y - a) % n;
}

Related

(java) convert a decimal number to binary without using parseint

I am new to java and I was learning how to convert from binary to decimal and vice versa. In the case of binary to decimal, I found out that I could use parseint, but I saw other methods that didn't use it, so I tried to implement them into my code, but it didn't work for me and I got stumped.
How would I be able to use a different method for calculating binary to decimal and implement it into my code?
Here is my code:
import java.util.Scanner;
class BinaryToDecimal {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
String binaryString;
char choice;
String nextLine = "Empty";
int i = 0;
choice = 'Y';
try {
do {
System.out.print("Enter a binary number: ");
binaryString = sc.nextLine();
//Find the string count
int count = binaryString.length();
for (int j = 0; j< count; j++)
{
if (binaryString.charAt(j) != '1' && binaryString.charAt(j) != '0')
{
System.out.print("Enter a binary number: ");
binaryString = sc.nextLine();
count = binaryString.length();
j=0;
}
}
i = Integer.parseInt(binaryString);
if (i>0)
System.out.println("The decimal number is: " + Integer.parseInt(binaryString, 2));
System.out.println("Continue using the calculator? Only input Y or N");
String ln = sc.next();
if(ln.length()==1){
choice = ln.charAt(0);
}
else{
choice = 'N';
}
if (sc.hasNextLine()) {
nextLine = sc.nextLine();
}
} while (choice == 'Y');
} catch (NumberFormatException nfe) {
System.out.println("Invalid input");
}
}
}
Binary math involves adding 1 and multiplying by 2. I would use a regular expression to test if the input is valid. I would use an infinite loop and break when the user gives an answer besides y when prompted to continue. Putting that together, gives a simplified
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("Enter a binary number: ");
String binaryString = sc.nextLine();
// An int value consists of up to 32 0 and 1s.
if (!binaryString.matches("[01]+") || binaryString.length() > 32) {
continue;
}
int v = 0;
for (int i = 0; i < binaryString.length(); i++) {
v *= 2;
if (binaryString.charAt(i) == '1') {
v++;
}
}
System.out.println("The decimal number is: " + v);
System.out.println("Continue using the calculator? Only input Y or N");
String ln = sc.nextLine();
if (!ln.equalsIgnoreCase("Y")) {
break;
}
}
It looks like your missing you're missing the radix which the default I use is 2. Try this and let me know what happens
i = Integer.parseInt(binaryString,2);
There may be a nicer way of doing this, however this is the solution that I came up with. I took into account that the number can both be a positive and negative number and added checks for those cases. I also made sure to add exceptions for when an invalid binary number is entered.
public static int numberFromBinary(String binaryNumber) {
char[] array = binaryNumber.toCharArray();
boolean isNegative = false;
int result = 0;
if (array.length > 32) {
throw new NumberFormatException("An integer cannot be more than 32 bits long.");
}
if (array.length == 32) {
isNegative = array[0] == '1';
if (isNegative) {
result -= 1;
}
}
for (int i = 0; i < array.length && i != 31; i++) {
int worth = (int) Math.pow(2, i);
if (array[array.length - 1] != '1' && array[array.length - 1] != '0') {
throw new NumberFormatException("Binary bits can only be a '1' or a '0'.");
}
if (isNegative) {
if (array[array.length - 1] == '0') {
result -= worth;
}
} else {
if (array[array.length - 1] == '1') {
result += worth;
}
}
}
return result;
}
Here's a solution for converting a string representation of a binary number to a decimal number, without using Integer.parseInt(). This is based on
your original question text:
How would I be able to use a different method for calculating binary to decimal and implement it into my code?
And also a comment you added:
Also i did not want to use parseint
If you take a binary number and work your way from right to left, each digit is an increasing power of 2.
0001 = 2^0 = 1
0010 = 2^1 = 2
0100 = 2^2 = 4
1000 = 2^3 = 8
You can follow this same pattern: inspect each character position of a binary string input, and raise 2 to some power to get the decimal value represented by that bit being set to 1. Here's a simple bit of code that:
prompts for user input as a binary string
starting from right and working toward the left, it checks each character, comparing against '1'
if the character is in fact 1: take note of the position, raise 2 to the next power, and add that to the running total
Here's the code:
System.out.print("enter a binary number: ");
String binaryInput = new Scanner(System.in).next();
int decimalResult = 0;
int position = 0;
for (int i = binaryInput.length() - 1; i >= 0; i--) {
if (binaryInput.charAt(i) == '1') {
decimalResult += Math.pow(2, position);
}
position++;
}
System.out.println(binaryInput + " --> " + decimalResult);
And a few sample runs:
enter a binary number: 1111
1111 --> 15
enter a binary number: 101010101
101010101 --> 341
enter a binary number: 100000000000
100000000000 --> 2048

Adding individual digits from a string

I'm trying to add 13 individual digits from a string together. I thought using a while loop would be the best way to do this, but I think I messed something up. Here's my code:
import java.util.Scanner;
public class ISBNChecker{
public static void main(String [] args){
String isbnNumber;
int isbnTotal= 0;
int index = 0;
Scanner scnr = new Scanner(System.in);
System.out.println("Enter a 13 digit ISBN Number:");
isbnNumber = scnr.nextLine();
if (isbnNumber.length() != 13) {
System.out.println("Error- 13 numerical digits required");
}
char num = isbnNumber.charAt(index);
while (index <13) {
if (index % 2 == 0) {
isbnTotal = isbnTotal + num;
index =index + 1;
}
else {
isbnTotal = isbnTotal + (3 * num);
index = index + 1;
}
}
System.out.println(isbnTotal);
if (isbnTotal % 10 == 0) {
System.out.println("Valid ISBN Number");
}
else {
System.out.println("Invalid ISBN Number");
}
}
}
I'm using the input 9780306406157, which should be an invalid ISBN Number. The value of isbnTotal at the end of the program should be 100, but instead, it is 1425. Any help in figuring out how to fix it would be appreciated.
Also, the formula I'm using for the problem is x1 + 3x2 + x3 + 3x4 ... +x 13 for reference!
I found your bug, you made some mistake.
int index = 0;
while (index < 13) {
char num = (char) (isbnNumber.charAt(index) - '0');
if (index % 2 == 0) {
isbnTotal = isbnTotal + num;
} else {
isbnTotal = isbnTotal + (3 * num);
}
index++;
}
Problem #1
The code
char num = isbnNumber.charAt(index);
Was not in your while loop, causing your code to always run with the same character.
Problem #2
When doing
char num = isbnNumber.charAt(index);
You are actually getting the ASCII value of the character. What you cant to get is the value of the number right ? So you have to do:
char num = (char) (isbnNumber.charAt(index) - '0');
Notice that the zero is between two single quote, that because we want the value of the ZERO ASCII CHARACTER (which is 38).
'1' - '0' = 1
'9' - '0' = 9
EDIT: I forgot to mention that you should check before if the character is a number, else you will maybe try to do something like 'A' - '0' which will be equal to 17
Here is a detailed working code for your ISBN checker
import java.util.Scanner;
public class ISBNChecker{
public static void main(String [] args){
String isbnNumber;
int isbnTotal= 0;
int index = 0;
Scanner scnr = new Scanner(System.in);
System.out.println("Enter a 13 digit ISBN Number:");
isbnNumber = scnr.nextLine();
if (isbnNumber.length() != 13) {
System.out.println("Error- 13 numerical digits required");
}
//Perform other operations if it's length is 13
else{
//initiliizing num for 1st time + convert the character at index to
number
int num = Character.getNumericValue(isbnNumber.charAt(index));
while (index <13) {
if (index % 2 == 0) isbnTotal = isbnTotal + num;
else isbnTotal = isbnTotal + (3 * num);
//increment outside of if else - less code
index = index + 1;
//verify if index is not out of bounds for charAt()
//then change num value + conversion
if(index<13 )
num = Character.getNumericValue(isbnNumber.charAt(index));
}
System.out.println(isbnTotal);
if (isbnTotal % 10 == 0) System.out.println("Valid ISBN Number");
else System.out.println("Invalid ISBN Number");
}
}
}
As mentioned in the answer below, nextLine() gets input in ASCII characters and it is important to convert that into numbers when there are numerical calculations involved.
Well, it seems that you do allow string inputs longer or shorter than 13.
Then you should return after this line:-
System.out.println("Error- 13 numerical digits required");
Otherwise, the code will run even if it's not at the wanted length

Algorithm will not encrypt/decrypt properly

I tested the below code with all ASCII values from 64 - 90 inclusive (All uppercase letters) and adjusted accordingly so instead of:
for(int i = 0 ; i < c.length(); i++){
info[i] = ((int)c.charAt(i) - 32);
}
I would replace the 32 with 64 (so the ASCII value of A would save in the array as 0). Furthermore, in my encryption and decryption functions I would replace 95 with 26 (26 letters).
However, if I apply this to all values between 32-126 inclusive (95 characters) and adjust the values accordingly, the values become incorrect and I don't know why. Here is my whole main function below (note that the formula used in encryption and decryption is just an example one I used and I plan on changing the values later on):
public static void main(String[] args) {
String c = "sd344rf"; // could be any set of characters within the range
int[] e = new int[c.length()]; // encrypted set
int[] d = new int[c.length()]; // decrypted set
int[] info = new int[c.length()];
for(int i = 0 ; i < c.length(); i++){
info[i] = ((int)c.charAt(i) - 32);
}
for(int i = 0; i < c.length(); i++){
e[i] = encryption(info[i]);
}
for(int i = 0; i < c.length(); i++){
d[i] = decryption(e[i]);
}
display(info);
System.out.println();
display(e);
System.out.println();
display(d);
}
public static int encryption(int x){
return mod(3*x + 9,95);
}
public static int decryption(int x){
return mod(9*x - 3,95);
}
public static void display(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i] + " ");
}
}
}
Obviously you are trying to implement an affine cipher. For an affine cipher the encryption is
y = mod(n * x + s, m)
and the decryption
x = mod(ni * (y - s), m)
with
x: Value of the character to encrypt
y: Value of the encrypted character
m: Number of characters in the underlying alphabet
n, s: Key of the encryption
n and s must be chosen so that they are between 0 and m - 1, inclusive. In addition, n has to be chosen so that n and m are coprime. ni is the modular multiplicative inverse of n modulo m and is determined by n*ni mod m = 1.
This is in more detail explained at https://en.wikipedia.org/wiki/Affine_cipher.
If the values u, v associated with the characters don't start at 0 the values have to be shifted by an offset equal to the value of the first character (provided that there are no gaps) and the formulas become
x = u - offset
y = v - offset
v = mod(n * (u - offset) + s, m) + offset
u = mod(ni * ((v - offset) - s), m) + offset
Thus, you've to replace in the main-method
info[i] = ((int)c.charAt(i) - 32);
with
info[i] = (int)c.charAt(i);
The encryption-method becomes:
public static int encryption(int u) {
return mod(n * (u - offset) + s, m) + offset;
}
and the decryption-method
public static int decryption(int v) {
return mod(ni * ((v - offset) - s), m) + offset;
}
with the fields
private static int m = <Number of the characters in the alphabet>;
private static int n = <Key (factor)>; // n between 0 and m-1 and moreover, n and m have te be coprime
private static int s = <Key (summand)>; // s between 0 and m-1
private static int offset = <Value of the first character of the alphabet>;
private static int ni = <Modular multiplicative inverse of n modulo m>;
Moreover, for the mod-operation the following method is used (see Encryption/decryption program not working properly):
private static int mod(int a, int b) {
return ((a % b) + b) % b;
}
Example 1: Uppercase letters A - Z:
private static int m = 'Z' - 'A' + 1; // 26
private static int n = 3; // Choose e.g. n = 3: n = 3 < 26 - 1 = 25 and moreover, 3 and 26 are coprime
private static int s = 9; // Choose e.g. s = 9: s = 9 < 26 - 1 = 25
private static int offset = 'A'; // 65
private static int ni = 9; // 3*9 mod 26 = 1
Test:
String c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Output (with characters instead of their values):
Plain text: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Encrypted text: JMPSVYBEHKNQTWZCFILORUXADG
Decrypted text: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Example 2: All characters between 32 (Space) and 126 (~), inclusive:
private static int m = '~' - ' ' + 1; // 95
private static int n = 3; // Choose e.g. n = 3: n = 3 < 95 - 1 = 94 and moreover, 3 and 95 are coprime
private static int s = 9; // Choose e.g. s = 9: s = 9 < 95 - 1 = 94
private static int offset = ' '; // 32
private static int ni = 32; // 3*32 mod 95 = 1
Test:
String c = " !\"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
Output (with characters instead of their values):
Plain text: !"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
Encrypted text: ),/258;>ADGJMPSVY\_behknqtwz}!$'*-0369<?BEHKNQTWZ]`cfilorux{~"%(+.147:=#CFILORUX[^adgjmpsvy| #&
Decrypted text: !"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

nth Binary palindrome with efficient time complexity

Given an integer N, i am trying to find the nth binary palindrome.I have written the following code but it is not efficient.is there a more efficient way in terms of time complexity.
I was trying it out as a problem online and i was supposed to output in 1 sec or less but for every input it takes 2 seconds.
public static Boolean Palind(String n){
String reverse = "";
int length = n.length();
for(int i = length - 1; i >=0;i--){
reverse = reverse + n.charAt(i);
}
if(n.equals(reverse)){
return true;
}
else{
return false;
}
}
public static int Magical(int n){
ArrayList<Integer> res = new ArrayList<Integer>();
for(int i = 1; i < Math.pow(2, n);i++){
if(Palind(Integer.toBinaryString(i))){
res.add(i);
}
}
return res.get(n-1);
}
The relevant OEIS entry (A006995) has a lot of nice tips if you read through it. For example, a(2^n-1)=2^(2n-2)-1 lets you skip right to the (2n - 1)th palindrome really quickly.
It also gives several implementations. For example, the Smalltalk implementation works like this (note that the input value, n, starts with 1 for the first palindrome, 0):
public static final int nthBooleanPalindrome(int n) {
if (n == 1) return 0;
if (n == 2) return 1;
int m = 31 - Integer.numberOfLeadingZeros(n);
int c = 1 << (m - 1);
int b;
if (n >= 3*c) {
int a = n - 3*c;
int d = 2*c*c;
b = d + 1;
int k2 = 1;
for (int i = 1; i < m; i++) {
k2 <<= 1;
b += a*k2/c%2*(k2 + d/k2);
}
}
else {
int a = n - 2*c;
int d = c*c;
b = d + 1 + (n%2*c);
int k2 = 1;
for (int i = 1; i < m - 1; i++) {
k2 <<= 1;
b += a*k2/c%2*(k2 + d/k2);
}
}
return b;
}
Try something like this maybe?
public static void main(String[] args) {
for (int i = 1; i < 65535; i++) {
System.out.println(
i + ": " + getBinaryPalindrom(i) + " = " + Integer.toBinaryString(getBinaryPalindrom(i)));
}
}
public static int getBinaryPalindrom(int N) {
if (N < 4) {
switch (N) {
case 1:
return 0;
case 2:
return 1;
case 3:
return 3;
}
throw new IndexOutOfBoundsException("You need to supply N >= 1");
}
// second highest to keep the right length (highest is always 1)
final int bitAfterHighest = (N >>> (Integer.SIZE - Integer.numberOfLeadingZeros(N) - 2)) & 1;
// now remove the second highest bit to get the left half of our palindrom
final int leftHalf = (((N >>> (Integer.SIZE - Integer.numberOfLeadingZeros(N) - 1)) & 1) << (Integer.SIZE -
Integer.numberOfLeadingZeros(N) - 2)) | ((N << (Integer.numberOfLeadingZeros(N) + 2)) >>> (Integer.numberOfLeadingZeros(N) + 2));
// right half is just the left reversed
final int rightHalf = Integer.reverse(leftHalf);
if (Integer.numberOfLeadingZeros(leftHalf) < Integer.SIZE / 2) {
throw new IndexOutOfBoundsException("To big to fit N=" + N + " into an int");
}
if (bitAfterHighest == 0) {
// First uneven-length palindromes
return (leftHalf << (Integer.SIZE - Integer.numberOfLeadingZeros(leftHalf)) - 1) | (rightHalf
>>> Integer.numberOfTrailingZeros(rightHalf));
} else {
// Then even-length palindromes
return (leftHalf << (Integer.SIZE - Integer.numberOfLeadingZeros(leftHalf))) | (rightHalf
>>> Integer.numberOfTrailingZeros(rightHalf));
}
}
The idea is that each number will become a palindrome once it reverse is added. To have the halves correctly aligned the halves just need to be shifted in place.
The problem why this has gotten a bit complex is that all uneven-length palindromes of a given leftHalf length come before all even-length palindromes of a given leftHalf length. Feel free to provide a better solution.
As int has 32 bit in Java there is a limit on N.
int-Version on ideone.com
And a BigInteger-version to support big values. It is not as fast as the int-version as the byte[]-arrays which store the value of the BigInteger create some overhead.
public static void main(String[] args) {
for (BigInteger i = BigInteger.valueOf(12345678); i.compareTo(BigInteger.valueOf(12345778)) < 0; i = i
.add(BigInteger
.ONE)) {
final BigInteger curr = getBinaryPalindrom(i);
System.out.println(i + ": " + curr + " = " + curr.toString(2));
}
}
public static BigInteger getBinaryPalindrom(BigInteger n) {
if (n.compareTo(BigInteger.ZERO) <= 0) {
throw new IndexOutOfBoundsException("You need to supply N >= 1");
} else if (n.equals(BigInteger.valueOf(1))) {
return BigInteger.valueOf(0);
} else if (n.equals(BigInteger.valueOf(2))) {
return BigInteger.valueOf(1);
} else if (n.equals(BigInteger.valueOf(3))) {
return BigInteger.valueOf(3);
}
final int bitLength = n.bitLength() - 1;
// second highest to keep the right length (highest is always 1)
final boolean bitAfterHighest = n.testBit(bitLength - 1);
// now remove the second highest bit to get the left half of our palindrom
final BigInteger leftHalf = n.clearBit(bitLength).setBit(bitLength - 1);
// right half is just the left reversed
final BigInteger rightHalf;
{
byte[] inArray = leftHalf.toByteArray();
byte[] outArray = new byte[inArray.length];
final int shiftOffset = Integer.SIZE - Byte.SIZE;
for (int i = 0; i < inArray.length; i++) {
outArray[inArray.length - 1 - i] = (byte) (Integer.reverse(inArray[i]) >>> shiftOffset);
}
rightHalf = new BigInteger(1, outArray).shiftRight(outArray.length * Byte.SIZE - bitLength);
}
if (!bitAfterHighest) {
// First uneven-length palindromes
return leftHalf.shiftLeft(bitLength - 1).or(rightHalf);
} else {
// Then even-length palindromes
return leftHalf.shiftLeft(bitLength).or(rightHalf);
}
}
I have the same idea with #Kiran Kumar: you should not count number one by one to find if it is a binary palindrome which is too slow, but rather find the internal pattern that number has.
List the number in binary string one by one, you can find the pattern:
0
1
11
101
1001
1111
...
1......1
And the following is some math problem:
We have 2^round_up((L-2)/2) palindrome of number with length L in binary format.
Sum up every shorter length number, we get following len to sum mapping:
for (int i = 1; i < mapping.length; i++) {
mapping[i] = (long) (mapping[i - 1] + Math.pow(2, Math.ceil((i - 1) * 1.0 / 2)));
}
If we find N range in [count(L), count(L+1)), we can concat it with remaining number:
public static long magical(long n) {
if (n == 0 || n == 1) {
return n;
}
long N = n - 2;
return Long.parseLong(concat(N), 2);
}
private static String concat(long N) {
int midLen = Arrays.binarySearch(indexRange, N);
if (midLen < 0) {
midLen = -midLen - 1;
}
long remaining = N - indexRange[midLen];
String mid = mirror(remaining, midLen);
return '1' + mid + '1';
}
private static String mirror(long n, int midLen) {
int halfLen = (int) Math.ceil(midLen * 1.0 / 2);
// produce fixed length binary string
final String half = Long.toBinaryString(n | (1 << halfLen)).substring(1);
if (midLen % 2 == 0) {
return half + new StringBuilder(half).reverse().toString();
} else {
return half + new StringBuilder(half).reverse().toString().substring(1);
}
}
Full code with test for produce large possible long can be found in my git repo.
Idea to optimize,
Let's look at the palindrome sequence 0, 1, 11, 101, 111, 1001 etc...
All numbers must begin and end with 1, So the middle bits only changes and midle substring should be palindrome for full string to become palindrome,
So let's take a 2 digit binary number - one palindrome is possible.
The binary of the decimal 3 is a palindrome. 11
For a 3 digit binary number 2 palindromes are possible, 2*(no of 1 digit palindrome)
The binary of the decimal 5 is a palindrome. 101
The binary of the decimal 7 is a palindrome. 111
For 5 digit binary number 4 palindromes are possible 2*(no of 3 digit palindrome)
10001,10101, 11011, 11111
and so on,
So it will be 2 + 20 + 21 + 22 +...... +2i-N ,
we solve for i and find out the palindrome number.
So by analysing this sequence we get an equation like 2(i/2)+1 -1 = N
where N is the No of palindrome,
and i is the number of bits in the nth palindrome string,
using this we can find the length of the String, from this we can find the string early.
This might be complex, but helps in solving higher values of N quickly....

Hi I want to print all the palindromic primes between 2 numbers the user inputs

So, I am done with first part and most of 2nd one but can not figure out how to do Palindromic primes, so please help. The code i am attaching is p1b and I have no idea what to do in p1c so please help.
I. Introduction:
The purpose of this project is to help you learn the basic JAVA programming language
elements based upon your C/C++ background that you gained from CSC114.
II. Assignment: Develop a JAVA program according to the following steps:
Part A: Name this step p1a.java and you don’t need to write a method other than main
for this.
1) The user is asked to enter an integer and your program is to produce the reverse
of that number.
Example follows:
Please enter an integer number: 2341
The reverse of 2341 is 1432
Part B: Name this step p1b.java.
1) Modify the last step using a method called reverse that returns the reverse value of
a given integer. Then, modify the program so that it will runs as the following:
2) The user is asked to enter two integers, say x and y;
3) So long as x is less than y, your program will do the following:
a) Find and print all prime numbers between x and y. (Need a method called
isPrime that checks whether a given number is a prime or not.)
b) Find and print all prime numbers that are also palindromes;
c) Ask for another pair of x and y.
Example follows:
Please enter two integer numbers: 5 400
Prime:
5 7 11 13 17 19 23 29 31 37
41 43 47 53 59 61 67 71 73 79
83 89 97 101 103 107 109 113 127 131
137 139 149 151 157 163 167 173 179 181
191 193 197 199 211 223 227 229 233 239
241 251 257 263 269 271 277 281 283 293
307 311 313 317 331 337 347 349 353 359
367 373 379 383 389 397
Palindromic Prime:
5 7 11 101 131 151 181 191 313 353
373 383
Please enter two integer numbers: 9 1
Done
Part C: Name this step p1c.java.
1) Traditionally, in order to produce the above output, the approach that one normally
has is to go through the numbers twice: the first time is to find and print all prime
numbers and then go through the numbers again to find and print all palindromic
primes. However, if we are able to keep the result and then print it when the
process is completed, then it will only need 1 pass.
2) Additionally, please display your output in a formatted way (such as aligned output
in the previous page).
3) Modify p1b.java so that it will only go through one pass to produce the same result
as produced in part B.
import java.util.Scanner;
public class p1b {
public static void main(String[] args) {
System.out.println("Program for Finding Primem Numbers: ");
System.out.println("==================================");
Scanner input = new Scanner(System.in);
int num1, num2, i;
int choice;
System.out.print("Please enter the first number: ");
num1 = input.nextInt();
System.out.print("Please enter the Second number: ");
num2 = input.nextInt();
if (num1 > num2) {
System.out.println(" Thank you.");
System.exit(1);
}
System.out.println( isPrime(num1, num2));
System.out.println( reverse(num1));
System.out.print("\nWould you like to enter two more numbers, "
+ "Enter 1 for Yes or 0 for No: ");
choice = input.nextInt();
if(choice == 1) {
System.out.print("Please enter the first number: ");
num1 = input.nextInt();
System.out.print("Please enter the Second number: ");
num2 = input.nextInt();
System.out.println( isPrime(num1, num2));
}
if(choice == 0) {
System.out.println("Bye!!");
System.exit(0);
}
}
public static int isPrime(int num1, int num2) {
final int displayPerLine = 50;
System.out.print("Prime: ");
for ( int i = num1; i <= num2; i++ ) {
int j;
for ( j = 2; j < i; j++) {
int number = i % j;
if (number == 0) {
break;
//return n;
}
}
if(i == j) {
System.out.printf("%-5d " + i);
}
if (i % displayPerLine == 0) {
System.out.println();
}
}
System.out.println();
return num1;
}
//public static int reverse(int num1, int num2) {
public static int reverse(int num1, int num2) {
System.out.print("Palindrome: ");
int palindrome = num1; // copied number into variable
int reverse = palindrome;
while (palindrome != 0) {
int remainder = palindrome % 10;
reverse = reverse * 10 + remainder;
palindrome = palindrome / 10;
}
// if original and reverse of number is equal means
// number is palindrome in Java
if (num1 == reverse) {
return reverse;
}
return palindrome;
}
public static int reverse(int num) {
int test = 0;
while (num != 0) {
int lastdigit = num % 10;
test = test * 10 + lastdigit;
num = num / 10;
}
return test;
}
public static boolean isPalindrome(int num) {
return num == reverse(num);
}
}
Let's start by fixing isPrime, first test if the number is even; if so, it isn't prime. Next, test odd numbers from 3 to the square root of the number. If none of those are factors, then the number is prime. Something like,
public static boolean isPrime(int i) {
if (i % 2 == 0) {
return false;
}
double sqrt = Math.sqrt(i);
for (int t = 3; t <= sqrt; t += 2) {
if (i % t == 0) {
return false;
}
}
return true;
}
Then we can implement isPalindrome with a StringBuilder like
public static boolean isPalindrome(int i) {
String str = String.valueOf(i);
return new StringBuilder(str).reverse().toString().equals(str);
}
Finally, we can prompt for input in an infinite (here a while) loop and use an inner for loop for printing primes that are also palindromes. And, you can use Math.max and Math.min for getting the max and min respectively. Like,
public static void main(String[] args) {
System.out.println("\t\tProgram for Finding Prime Numbers: ");
System.out.println("\t\t===================================");
Scanner input = new Scanner(System.in);
while (true) {
System.out.print("Please enter the first number: ");
int num1 = input.nextInt();
System.out.print("Please enter the second number: ");
int num2 = input.nextInt();
int lo = Math.min(num1, num2);
int hi = Math.max(num1, num2);
for (int i = lo; i <= hi; i++) {
if (isPalindrome(i) && isPrime(i)) {
System.out.println(i);
}
}
System.out.print("Would you like to enter two more numbers, "
+ "Enter 1 for Yes or 0 for No: ");
int choice = input.nextInt();
if (choice == 0) {
break;
}
}
}

Categories

Resources