I'm working on a problem where I'm required to manipulate large lists of palindromes up to a certain number of digits. This should work with numbers up 15 digits. The most common method I've seen for this is iterating through each number and checking whether each is a palindrome and then adding that to a list. This is my implementation in java and it works fine.
public class Palindrome {
public ArrayList<Long> List = new ArrayList<Long>();
public double d;
public Palindrome(double digits) {
this.d = digits;
long dig = (int)(Math.pow(10,digits));
for (long i = 1; i <= dig; i++) {
long a = i;
long b = inverse(a);
if (a == b) {
List.add(a);
}
}
public long inverse(long x){
long inv = 0;
while (x > 0) {
inv = inv * 10 + x % 10;
x = x / 10;
}
return inv;
}
}
Only problem is it's pretty slow when I get to 10+ digit palindromes. I've been considering alternative ways to create this list and one consideration I've had is generating the list of palindromes rather than iterating through each number and checking if it's a palindrome.
I'm still working on paper but the pattern isn't as obvious as I thought I would find it to turn into pseudocode. I'm working it out that for n number of digits, going from i to n, if the number of digits is even, generate numbers from 1 up to [10^(i/2 + 1) - 1]. Then append the reverse of each number to itself. A little stuck on how to do it for the odd digits. That's where I am right now.
I will come back with my own response if I figure this out and implement the code but in the meantime, I would just like to know if anyone has done this before or has an alternative method I've overlooked that would be more efficient.
UPDATE
So I did manage to work out something thanks to all your suggestions. I decided to work with the numbers as strings but contrary to what I intended this has actually increased the runtime :/
public class Palindrome2 {
public ArrayList<Long> List = new ArrayList<Long>();
public double d;
public Palindrome2(double digits) {
this.d = digits;
for (long n = 1; n <= d; n++) {
if (n == 1) {
for (long i = 1; i < 10; i++) {
List.add(i);
}
}
if (n % 2 != 0 && n != 1) {
long max = (long) Math.pow(10, (n + 1) / 2);
long min = (long) Math.pow(10, Math.floor(n / 2));
for (long i = min; i < max; i++) {
String str = Long.toString(i);
str = str + removeFirst(reverse(str));
Long x = Long.parseLong(str);
List.add(x);
}
} else if (n % 2 == 0) {
long max = (long) (Math.pow(10, Math.floor((n + 1) / 2)) - 1);
long min = (long) Math.pow(10, (n / 2) - 1);
for (long i = min; i <= max; i++) {
String str = Long.toString(i);
str = str + reverse(str);
Long x = Long.parseLong(str);
List.add(x);
}
}
}
}
public String reverse(String x) {
String rev = new StringBuffer(x).reverse().toString();
return rev;
}
public String removeFirst(String x) {
return x.substring(1);
}
}
Once again, accurate but still slow :(
Introduction
You need to analyzing the regular pattern for an algorithm roughly before jump into developing, that will saving lot of time, for example:
each 1 digit is 1 palindrome, e.g: 1
each 2 digits has 1 palindrome, e.g: 11.
each 3 digits has 10 palindromes, e.g: 101,111,...,191.
each 4 digits has 10 palindromes, e.g: 1001, 1111, ..., 1991.
each 5 digits has 100 palindromes, e.g: 10001, 11011, ..., 19091, ..., 19991.
each 6 digits has 100 palindromes, e.g: 100001, 110011, ..., 190091, ..., 199991.
each 7 digits has 1000 palindromes, e.g: 1000001, ...,1900091,...,1090901, ..., 1999991.
each 8 digits has 1000 palindromes, e.g: 10000001, ...,19000091,...,10900901, ..., 19999991.
....
then you can write some arrangement algorithm to implement this .
Implementation
But I can tell you this implementation can optimizing as further, if you using a cache to saving palindromes generated from low digits palindromes(2), then any high digits palindromes(n>2) can reusing it.
Maybe it's not robust but it pass all my tests on github. I left the rest working & optimization to you, and I wish you can done by yourself.
private static List<Integer> palindromes(int digits) {
return palindromes(digits, 0);
}
private static List<Integer> palindromes(int digits, int shifts) {
List<Integer> result = new ArrayList<>();
int radix = (int) Math.pow(10, digits - 1);
int renaming = digits - 2;
boolean hasRenaming = renaming > 0;
for (int i = start(digits, shifts); i <= 9; i++) {
int high = i * radix;
int low = low(digits, i);
if (hasRenaming) {
for (Integer m : palindromes(renaming, shifts + 1)) {
int ret = high + m * 10 + low;
if (ret < 0) {
return result;
}
result.add(ret);
}
} else {
result.add(high + low);
}
}
return result;
}
private static int low(int digits, int high) {
return digits > 1 ? high : 0;
}
private static int start(int digits, int shifts) {
return digits > 1 && shifts == 0 ? 1 : 0;
}
Usage
then you can collect all palindrome numbers as below:
// v--- min:0, max: 2147447412, count: 121474
List<Integer> all = IntStream.rangeClosed(1, 10)
.mapToObj(PalindromeTest::palindromes)
.flatMap(List::stream)
.collect(Collectors.toList());
Time Cost:
191ms
Enable Caching
public class Palindromes {
private static final int[] startingNonZerosTable = {
0,// 0
0, 1,// 1 2
10, 10,//3 4
100, 100, //5 6
1000, 1000,//7 8
10000, 10000,// 9 10
100000, 100000,//11 12
1000000, 1000000,//13 14
10000000, 10000000,//15 16
100000000, 100000000,//17 18
1000000000, 1000000000//19 20
};
private static final int MAX_DIGIT = 9;
private static final int MIN_DIGIT = 0;
private static final int RADIX = MAX_DIGIT - MIN_DIGIT + 1;
private static final int LONG_MAX_DIGITS = 19;
private static volatile long[][] cache = new long[LONG_MAX_DIGITS + 1][];
// includes palindromes(0) ---^
static {
cache[0] = new long[0];
cache[1] = new long[]{0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L};
cache[2] = new long[]{0L, 11L, 22L, 33L, 44L, 55L, 66L, 77L, 88L, 99L};
}
public static LongStream since1(int end) {
return between(1, end);
}
public static LongStream between(int start, int end) {
return IntStream.rangeClosed(start, end)
.mapToObj(Palindromes::of)
.flatMapToLong(identity());
}
public static LongStream of(int digits) {
return Arrays.stream(palindromes0(digits))
.skip(startingNonZerosTable[digits]);
}
private final static long[] palindromes0(int digits) {
if (cache[digits] != null) {
return cache[digits];
}
long[] result = new long[sizeOf(digits)];
int size = 0;
long high = (long) Math.pow(RADIX, digits - 1);
for (int i = MIN_DIGIT; i <= MAX_DIGIT; i++) {
for (long mid : palindromes0(digits - 2)) {
long value = i * high + mid * RADIX + i;
if (value < 0) {//overflow
return cache[digits] = Arrays.copyOf(result, size);
}
result[size++] = value;
}
}
return cache[digits] = result;
}
private static int sizeOf(int digits) {
return MAX_DIGIT * (int) Math.pow(RADIX, (digits - 1) >>> 1)
+ startingNonZerosTable[digits];
}
// v--- java -Xms1024m -Xmx2048m test.algorithm.Palindromes
public static void main(String[] args) {
Duration duration = timing(() -> {
// palindromes[1..15] ---v
LongSummaryStatistics result = since1(15).summaryStatistics();
long max = result.getMax();
long count = result.getCount();
System.out.printf("Max: %d, Count: %d%n", max, count);
});
System.out.printf("Time Elapsed:%s%n", duration);
// ^--- time elapsed: 4s
}
private static Duration timing(Runnable task) {
long starts = System.currentTimeMillis();
task.run();
return Duration.ofMillis(System.currentTimeMillis() - starts);
}
}
Time Cost:
palindromes[1..15] time elapsed: 4s
Have you tried working with characters rather than numbers? You could generate the palindrome as a string of digits and then convert to a number at the end. Something like this pseudocode:
generatePalindrome(size)
half <- size DIV 2 // Integer division
result <- ""
result.append(randomDigitIn(1..9)) // No leading zeros.
while (result.length <= half)
result.append(randomDigitIn(0..9))
endwhile
if (size is odd)
result <- result + randomDigitIn(0..9) + result.reverse()
else
result <- result + result.reverse()
endif
return number.parse(result)
end generatePalindrome()
Basically you randomly generate half the palindrome, avoiding leading zeros, insert an extra digit in the middle for odd lengths, append the reversed first half and then parse the digit string into the number format you want.
For odd digit you can simply reuse the palindromes generated at the previous even step , split them in half and insert in the middle all the possible number from 0 to 9.
Let's say you need to generate the palindrom of 3 digit, simply get all the palindromes of 2 digit and add insert all the number from 0 to 9.
We have 22 than we can generate:
202
212
222
232
and so on
Hope my idea is clear:)
Try something like this:
public class Palindrome
{
public static ArrayList<Long> calculatePalindromes(int maxLength) {
ArrayList<Long> result = new ArrayList<>();
if (maxLength <= 0) {
return result;
}
long maxPart = (long)Math.pow(10, maxLength / 2);
for (long i = 0; i < 10; ++i) {
result.add(i);
}
for (long i = 1; i < maxPart; ++i) {
long curHalf = i;
long curNum = i;
int curLen = 0;
while (curHalf != 0) {
curNum *= 10;
curNum += curHalf % 10;
curHalf /= 10;
++curLen;
}
result.add(curNum);
// insert numbers from 0 to 9
if (curLen * 2 + 1 > maxLength) {
continue;
}
for (int j = 0; j < 10; ++j) {
curHalf = i;
curNum = i;
curNum *= 10;
curNum += j;
while (curHalf != 0) {
curNum *= 10;
curNum += curHalf % 10;
curHalf /= 10;
}
result.add(curNum);
}
}
return result;
}
}
The idea is to insert numbers from 0 to 9 after each X and add reversed(X) after it so we get X (1..9) reversed(X).
You can generate all palindromes in the needed range without check, but you will probably face with the memory insufficiency, as storing all these numbesr for 15-length upper number in the list - is a bad idea.
More specifically your code will looks like:
long dig = (long) Math.pow(10, digits / 2);
int pow = 10;
int npow = 100;
for (long i = 1; i <= dig; i++) {
System.out.println(i * pow + inverse(i));
System.out.println(i * pow / 10 + inverse(i / 10));
// list.add(i * pow + inverse(i));
// list.add(i * pow/10 + inverse(i / 10));
if (i % pow == 0) {
pow = npow;
npow *= 10;
}
}
I have deliberately commented list adding lines.
The idea is to push into list/output all numbers composed with given half as:
XXXY+YXXX
and
XXX+Y+XXX
i.e. generating both cases: odd and even palindromes.
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....
import java.util.Scanner;
public class NeumannsRandomGenerator {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("Enter cases:");
int cases = in.nextInt();
int iterations = 0;
for (int i = 0; i <= cases; i++) {
int a = in.nextInt();
int res = ((a * a) / 100) % 10000;
if(res == a){
iterations++;
}
do {
int b = ((res * res) / 100) % 10000;
iterations++;
b = res;
} while (a != res);
System.out.println(iterations);
}
}
}
I am trying to figure Neumans random generator
For Example:
5761 - let it be the first number
5761 * 5761 = 33189121 - raised to power 2
33(1891)21 => 1891 - truncate to get the middle
1891 - it is the second number in the sequence
1891 * 1891 = 3575881 - raised to power 2 (add leading zero to get 8 digits)
03(5758)81 => 5758 - truncate to get the middle
5758 - it is the third number in the sequence (and so on...)
Please help why I am not getting any results:(
I don't know the rules for an iteration where your number is padded like 0315. If the padded value would be 00099225, and the resulting number is 992, then this would be a good start. I also don't see your rule for re-generating, so it just recurses.
I'm doing this quickly, so there could be some unnecessary code here
public int getNeumansRandomNumber(int starting) {
int nsquared = (int) Math.pow(starting, 2);
int length = String.valueOf(nsquared).length();
if (length < 8) {
String zeroPad = "00000000";
String padded = zeroPad.substring(length) + nsquared;
System.out.println("padded="+padded);
nsquared = Integer.valueOf(padded);
}
int middle = (nsquared % 1000000) / 100;
System.out.println(middle);
return getNeumansRandomNumber(middle);
}
References: 1, 2, and 3
CUSIPs are a 9-digit alphanumeric code for uniquely identifying a financial security.
https://en.wikipedia.org/wiki/CUSIP
They were invented in the 1964, and given the reliability of data transmission in the 60's, the 9th digit is actually a check digit used to confirm the validity of the first 8 characters. Sometimes, even today, you might find reason to want to validate a CUSIP, or perhaps a company or service obnoxiously decides to only transmit the 8-character CUSIP, even though this defeats the purpose of a check digit.
The procedure to generate the check digit is:
Convert non-numeric digits to values according to their ordinal position in the alphabet plus 9 (A=10, B=11,...Z=35) and converting the characters *=36, #=37, #=38.
Multiply every even digit by 2
If the result of the multiplication is a two-digit number, add the digits together. (12 = 1 + 2 = 3)
Get the sum of all values.
Get the floored value of this operation: (10 - (sum modulo 10)) modulo 10.
What is the best/simplest way to get this value in C#?
public string GenerateCheckDigit(string cusip)
{
int sum = 0;
char[] digits = cusip.ToUpper().ToCharArray();
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ*##";
for (int i = 0; i < digits.Length; i++)
{
int val;
if (!int.TryParse(digits[i].ToString(), out val))
val = alphabet.IndexOf(digits[i]) + 10;
if ((i % 2) != 0)
val *= 2;
val = (val % 10) + (val / 10);
sum += val;
}
int check = (10 - (sum % 10)) % 10;
return check.ToString();
}
Edit:
.NET Fiddle demonstrating this: https://dotnetfiddle.net/kspQWl
If you pre-compute the values of check digits, and store them in a lookup table, your computation of check digit would become much simpler:
private static readonly int[,] Check = new int[128, 2];
static CusipCheckSum() {
var cusipChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*##";
for (var i = 0 ; i != cusipChars.Length ; i++) {
Check[cusipChars[i], 0] = i%10 + i/10;
Check[cusipChars[i], 1] = 2*i%10 + 2*i/10;
}
}
With the 2D lookup array in place you can compute check digit in a single line of code:
var checkDigit = (10-(cusip.Select((ch, pos) => Check[ch, pos%2]).Sum()%10))%10;
I see that there is no algo for java so adding it as well:
String generateCusipCheckDigit(String cusip) {
final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ*##";
int sum = 0;
int value = 0;
char[] cusipChars = cusip.toUpperCase().toCharArray();
for (int i = 0; i < cusipChars.length; i++) {
char actualChar = cusipChars[i];
if (Character.isDigit(actualChar)) {
value = Integer.parseInt(String.valueOf(actualChar));
} else if (Character.isAlphabetic(actualChar)){
value = alphabet.indexOf(actualChar) + 10;
} else if (cusipChars[i] == '*'){
value = 36;
} else if (cusipChars[i] == '#'){
value = 37;
} else if (cusipChars[i] == '#'){
value = 38;
}
if ((i % 2) != 0){
value *= 2;
}
value = (value % 10) + (value / 10);
sum += value;
}
int check = (10 - (sum % 10)) % 10;
return String.valueOf(check);
}
and some tests:
#Test
void checkDigitTest1(){
String actual = generator.generateCusipCheckDigit("925524BF");
Assertions.assertEquals("6", actual);
}
#Test
void checkDigitTest2(){
String actual = generator.generateCusipCheckDigit("90284B96");
Assertions.assertEquals("2", actual);
}
#Test
void checkDigitTest3(){
String actual = generator.generateCusipCheckDigit("90284B97");
Assertions.assertEquals("0", actual);
}