[英]nth Binary palindrome with efficient time complexity
給定一個整數N,我試圖找到第n個二進制回文式。我編寫了以下代碼,但是效率不高。在時間復雜度方面是否有更有效的方法? 我正在嘗試將其作為在線問題進行處理,我應該在1秒或更短的時間內輸出,但每次輸入都需要2秒。
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);
}
嘗試類似的東西嗎?
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));
}
}
這個想法是,每個數字一旦加反就將成為回文。 為了使兩半正確對齊,只需將兩半移到適當的位置即可。
為什么變得有點復雜的問題是,給定leftHalf
長度的所有不均勻長度回文比所有給定leftHalf
長度的均勻長度回文leftHalf
。 隨時提供更好的解決方案。
由於int
在Java中具有32位,因此對N
有限制。
int
-Version上ideone.com
還有一個BigInteger
支持大價值。 它不如int
-version快,而存儲BigInteger
值的byte[]
-arrays會產生一些開銷。
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);
}
}
如果您閱讀過相關的OEIS條目(A006995),則有很多不錯的技巧。 例如, a(2^n-1)=2^(2n-2)-1
可以讓您真正快速地跳至第(2 n -1) 個回文。
它還提供了幾種實現。 例如,Smalltalk實現的工作方式如下(請注意,第一個回文數0
的輸入值n
以1
開頭):
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;
}
優化的想法,讓我們看看回文序列0、1、11、101、111、1001等。
所有數字都必須以1開始和結束,因此中間位僅會發生變化,並且中間子串應為回文,以使整個字符串成為回文,
因此,讓我們采用2位二進制數-一個回文是可能的。
十進制3的二進制是回文。 11
對於3位二進制數,可能有2個回文, 2 *(1位回文中沒有)
十進制的二進制5是回文。 101
十進制的7的二進制是回文。 111
對於5位數的二進制數,可能有4個回文數2 *(3位數的回文數沒有)
10001、10101、11011、11111
等等,
因此它將是2 + 2 0 + 2 1 + 2 2 + ...... +2 iN ,
我們為我求解並找出回文數。
因此,通過分析此序列,我們得到了一個等式2 (i / 2)+1 -1 = N
其中N是回文數
我是第n個回文字符串中的位數,
使用它我們可以找到字符串的長度,從中我們可以盡早找到字符串。
這可能很復雜,但是有助於快速解決更高的N值。
我對@Kiran Kumar有着相同的想法:您不應該一一計算數字是否是太慢的二進制回文,而應該找到數字的內部模式。
逐一列出二進制字符串中的數字,您可以找到模式:
0
1
11
101
1001
1111
...
1......1
以下是一些數學問題:我們有2^round_up((L-2)/2)
回文數,其二進制數的長度為L
總結每個較短的長度數字,我們得到以下len
到sum
映射:
for (int i = 1; i < mapping.length; i++) {
mapping[i] = (long) (mapping[i - 1] + Math.pow(2, Math.ceil((i - 1) * 1.0 / 2)));
}
如果我們在[count(L), count(L+1))
找到N
范圍,則可以將其與剩余數字連接起來:
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);
}
}
可以在我的git repo中找到完整的帶有測試的完整代碼,以產生可能的long
時間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.