大数除法:十进制字符串转化成不同的进制(java)
实战演练:有一个十进制字符串(长度不限),要将该字符串转化为52进制字符串,52进制字符由a-z和A-Z构成
一、十进制转换成52进制
1. 使用BigInteger
直接上代码
1 import java.math.BigInteger; 2 3 public class DecimalToBase52 { 4 5 // 52 进制字符映射:0->'a', 1->'b', ..., 25->'z', 26->'A', ..., 51->'Z' 6 private static final char[] DIGITS = createDigits(); 7 8 private static char[] createDigits() { 9 char[] d = new char[52]; 10 int idx = 0; 11 for (char c = 'a'; c <= 'z'; c++) d[idx++] = c; 12 for (char c = 'A'; c <= 'Z'; c++) d[idx++] = c; 13 return d; 14 } 15 16 /** 17 * 将非负十进制字符串转换为 52 进制字符串(使用 a-zA-Z 作为数字字符)。 18 * 19 * @param decimalStr 非负十进制整数的字符串表示(不能有前导 '+',可以有 "0") 20 * @return 对应的 52 进制字符串 21 * @throws IllegalArgumentException 当输入为 null、空字符串、包含非数字字符或为负时抛出 22 */ 23 public static String decimalToBase52(String decimalStr) { 24 if (decimalStr == null) throw new IllegalArgumentException("input is null"); 25 decimalStr = decimalStr.trim(); 26 if (decimalStr.isEmpty()) throw new IllegalArgumentException("input is empty"); 27 28 // 不允许负数 29 if (decimalStr.startsWith("-")) { 30 throw new IllegalArgumentException("negative numbers not supported"); 31 } 32 33 // 验证全为数字 34 if (!decimalStr.matches("\\d+")) { 35 throw new IllegalArgumentException("input must be a non-negative integer string"); 36 } 37 38 // 处理 0 的特殊情况 39 if (decimalStr.equals("0")) { 40 return String.valueOf(DIGITS[0]); // "a" 表示 0 41 } 42 43 BigInteger value = new BigInteger(decimalStr); 44 BigInteger base = BigInteger.valueOf(52); 45 46 StringBuilder sb = new StringBuilder(); 47 while (value.compareTo(BigInteger.ZERO) > 0) { 48 BigInteger[] divmod = value.divideAndRemainder(base); 49 int rem = divmod[1].intValue(); // 0..51 50 sb.append(DIGITS[rem]); 51 value = divmod[0]; 52 } 53 54 return sb.reverse().toString(); 55 } 56 57 // 简单示例与测试 58 public static void main(String[] args) { 59 String[] tests = { 60 "0", 61 "1", 62 "51", 63 "52", 64 "53", 65 "123456789", 66 "9223372036854775807", // long max 67 "123456789012345678901234567890" // 很大的数 68 }; 69 70 for (String t : tests) { 71 System.out.printf("decimal=%s -> base52=%s%n", t, decimalToBase52(t)); 72 } 73 } 74 }
说明与示例输出(示例运行结果)
(映射规则:0->a
,1->b
,...,25->z
,26->A
,...,51->Z
)
假如运行上面 main
,你可能会看到类似输出(具体长数的结果为示例):
注意:52
转换为 ba
(因为 52 = 1*52 + 0,对应 rem=0 -> 'a',商=1 -> 'b'),这与常见的“位中最低位在右侧”规则一致。
2、不用BigInteger例子
如果不用 BigInteger
,那就要自己实现大数除法,直接在字符串上操作。核心思路是:
-
输入是一个 十进制字符串(任意长度)。
-
模拟除法:
-
每次用字符串模拟 “除以 52”,得到 商字符串 和 余数 int。
-
把余数作为当前位(base52 的一位)。
-
把商字符串继续作为下一轮的被除数。
-
-
循环直到商为
"0"
。 -
把余数序列倒置,就是最终的 52 进制表示。
直接上代码
1 public class DecimalToBase52String { 2 3 // 52进制字符映射 4 private static final char[] DIGITS = createDigits(); 5 6 private static char[] createDigits() { 7 char[] d = new char[52]; 8 int idx = 0; 9 for (char c = 'a'; c <= 'z'; c++) d[idx++] = c; 10 for (char c = 'A'; c <= 'Z'; c++) d[idx++] = c; 11 return d; 12 } 13 14 /** 15 * 将十进制字符串转换为52进制字符串(不使用BigInteger) 16 */ 17 public static String decimalToBase52(String decimalStr) { 18 if (decimalStr == null || decimalStr.isEmpty()) { 19 throw new IllegalArgumentException("input is empty"); 20 } 21 if (decimalStr.equals("0")) { 22 return String.valueOf(DIGITS[0]); 23 } 24 25 StringBuilder result = new StringBuilder(); 26 27 String current = decimalStr; 28 while (!current.equals("0")) { 29 int[] divmod = divmod(current, 52); 30 int remainder = divmod[1]; 31 result.append(DIGITS[remainder]); 32 current = String.valueOf(divmod[0]); 33 } 34 35 return result.reverse().toString(); 36 } 37 38 /** 39 * 模拟字符串 ÷ base,返回 [商, 余数] 40 */ 41 private static int[] divmod(String number, int base) { 42 StringBuilder quotient = new StringBuilder(); 43 int remainder = 0; 44 45 for (int i = 0; i < number.length(); i++) { 46 int digit = number.charAt(i) - '0'; 47 int acc = remainder * 10 + digit; 48 int q = acc / base; 49 remainder = acc % base; 50 if (!(quotient.length() == 0 && q == 0)) { 51 quotient.append(q); 52 } 53 } 54 55 if (quotient.length() == 0) { 56 quotient.append("0"); 57 } 58 59 return new int[]{Integer.parseInt(quotient.toString()), remainder}; 60 } 61 62 public static void main(String[] args) { 63 String[] tests = { 64 "0", 65 "1", 66 "51", 67 "52", 68 "53", 69 "123456789", 70 "98765432109876543210" 71 }; 72 73 for (String t : tests) { 74 System.out.printf("decimal=%s -> base52=%s%n", t, decimalToBase52(t)); 75 } 76 } 77 }
直接对上面的decimalToBase52和divmod方法进行合并。代码如下:
1 public static String decimalToBase(char[] digits, String decimalStr) { 2 if(decimalStr == null || decimalStr.isEmpty()) { 3 return ""; 4 } 5 if(digits == null || digits.length <= 1) { 6 throw new IllegalArgumentException("digits is invalid"); 7 } 8 9 int base = digits.length; 10 String dividend = decimalStr; 11 StringBuilder result = new StringBuilder(); 12 while(dividend.compareTo("0") > 0) { 13 StringBuilder quotient = new StringBuilder(); 14 int remainder = 0; 15 for(int i = 0; i < dividend.length(); ++i) { 16 int tempDividend = 10 * remainder + (dividend.charAt(i) - '0'); 17 18 int q = tempDividend / base; 19 remainder = tempDividend % base; 20 if(quotient.length() != 0 || q != 0) { 21 quotient.append(q); 22 } 23 } 24 result.append(digits[remainder]); 25 26 dividend = quotient.toString(); 27 } 28 29 if (result.length() == 0) { 30 result.append(digits[0]); 31 } 32 33 return result.reverse().toString(); 34 }
⚠️ 关键点说明
-
divmod
里面要做的是 逐位除法:-
acc = remainder * 10 + 当前位
-
q = acc / base
-
remainder = acc % base
-
把
q
拼接进商字符串。
-
-
商可能很长,不能转成 int 存下来;我上面代码里
return new int[]{Integer.parseInt(...), remainder}
只能处理小数。如果你要支持任意长度的数,divmod
应该返回:
1 private static DivResult divmod(String number, int base) { 2 StringBuilder quotient = new StringBuilder(); 3 int remainder = 0; 4 5 for (int i = 0; i < number.length(); i++) { 6 int digit = number.charAt(i) - '0'; 7 int acc = remainder * 10 + digit; 8 int q = acc / base; 9 remainder = acc % base; 10 if (!(quotient.length() == 0 && q == 0)) { 11 quotient.append(q); 12 } 13 } 14 15 if (quotient.length() == 0) { 16 quotient.append("0"); 17 } 18 19 return new DivResult(quotient.toString(), remainder); 20 } 21 22 static class DivResult { 23 String quotient; 24 int remainder; 25 DivResult(String q, int r) { this.quotient = q; this.remainder = r; } 26 }
结果输出跟BigInteger例子相同
二、52进制转换成十进制
1. 使用BigInteger
1 import java.math.BigInteger; 2 3 public class Base52ToDecimal { 4 5 private static final char[] DIGITS = createDigits(); 6 7 private static char[] createDigits() { 8 char[] d = new char[52]; 9 int idx = 0; 10 for (char c = 'a'; c <= 'z'; c++) d[idx++] = c; 11 for (char c = 'A'; c <= 'Z'; c++) d[idx++] = c; 12 return d; 13 } 14 15 private static int charToVal(char c) { 16 if ('a' <= c && c <= 'z') return c - 'a'; 17 if ('A' <= c && c <= 'Z') return c - 'A' + 26; 18 throw new IllegalArgumentException("invalid char: " + c); 19 } 20 21 public static String base52ToDecimal(String base52Str) { 22 BigInteger result = BigInteger.ZERO; 23 BigInteger base = BigInteger.valueOf(52); 24 25 for (int i = 0; i < base52Str.length(); i++) { 26 int val = charToVal(base52Str.charAt(i)); 27 result = result.multiply(base).add(BigInteger.valueOf(val)); 28 } 29 return result.toString(); 30 } 31 32 public static void main(String[] args) { 33 System.out.println("ba -> " + base52ToDecimal("ba")); // 52 34 System.out.println("LbApu -> " + base52ToDecimal("LbApu")); // 123456789 35 System.out.println("bTq9wxSArfscvE (invalid chars?)"); 36 } 37 }
2.使用BigInteger
思路:
-
用一个字符串
result = "0"
保存十进制数。 -
遍历 base52 字符串:
-
其中
result * 52
和+ val
都需要自己写 大整数字符串运算。
实现如下:
1 public class Base52ToDecimalNoBigInt { 2 3 // 映射表 4 private static final char[] DIGITS = createDigits(); 5 6 private static char[] createDigits() { 7 char[] d = new char[52]; 8 int idx = 0; 9 for (char c = 'a'; c <= 'z'; c++) d[idx++] = c; 10 for (char c = 'A'; c <= 'Z'; c++) d[idx++] = c; 11 return d; 12 } 13 14 private static int charToVal(char c) { 15 if ('a' <= c && c <= 'z') return c - 'a'; 16 if ('A' <= c && c <= 'Z') return c - 'A' + 26; 17 throw new IllegalArgumentException("invalid char: " + c); 18 } 19 20 // 字符串大整数 * int 21 private static String multiply(String num, int factor) { 22 StringBuilder sb = new StringBuilder(); 23 //进位 24 int carry = 0; 25 for (int i = num.length() - 1; i >= 0; i--) { 26 //当前位*因子+进位 27 int prod = (num.charAt(i) - '0') * factor + carry; 28 //将当前位处理完后的个位放到结果中 29 sb.append(prod % 10); 30 //而十位以上为新的进位 31 carry = prod / 10; 32 } 33 34 //当进位大于0,说明还要增加最高位 35 while (carry > 0) { 36 sb.append(carry % 10); 37 carry /= 10; 38 } 39 return sb.reverse().toString(); 40 } 41 42 // 字符串大整数 + int 43 private static String add(String num, int addend) { 44 StringBuilder sb = new StringBuilder(); 45 int carry = addend; 46 for (int i = num.length() - 1; i >= 0; i--) { 47 int sum = (num.charAt(i) - '0') + carry; 48 sb.append(sum % 10); 49 carry = sum / 10; 50 } 51 while (carry > 0) { 52 sb.append(carry % 10); 53 carry /= 10; 54 } 55 return sb.reverse().toString(); 56 } 57 58 public static String base52ToDecimal(String base52Str) { 59 String result = "0"; 60 for (int i = 0; i < base52Str.length(); i++) { 61 int val = charToVal(base52Str.charAt(i)); 62 //比如52进制为CBA,那其实就是 ((0 * 52 + 27)*52 + 26) * 52 + 25,也即每一次结果*52加上当前位。也就是result = result*52 + val 63 result = multiply(result, 52); 64 result = add(result, val); 65 } 66 return result; 67 } 68 69 public static void main(String[] args) { 70 System.out.println("ba -> " + base52ToDecimal("ba")); // 52 71 System.out.println("LbApu -> " + base52ToDecimal("LbApu")); // 123456789 72 } 73 }
三、任何进制转换
常见的两种方法
方法一:中转十进制
这是最通用、最常见的思路。
-
先把 源进制(例如 base52)转成 十进制大整数(用
BigInteger
或字符串模拟)。 -
再把十进制数转成 目标进制(例如 base8)。
👉 适用于:任意进制之间转换。
方法二:直接基数转换
如果两个进制之间有 整倍数关系(比如 2 进制 ↔ 8 进制、2 进制 ↔ 16 进制),就可以直接按位分组映射,效率更高。
-
例子:二进制
110101
→ 按 3 位一组 → 八进制65
。
但是 52
和 8
没有整倍关系,所以只能用 方法一。