大数除法:十进制字符串转化成不同的进制(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,你可能会看到类似输出(具体长数的结果为示例):

decimal=0 -> base52=a
decimal=1 -> base52=b
decimal=51 -> base52=Z
decimal=52 -> base52=ba    // 52 => (1*52 + 0) => digits [1,0] -> 'b''a'
decimal=53 -> base52=bb
decimal=123456789 -> base52=??...   // 程序会输出具体字符串
decimal=9223372036854775807 -> base52=...
decimal=123456789012345678901234567890 -> base52=... (任意长整数均可)

注意:52 转换为 ba(因为 52 = 1*52 + 0,对应 rem=0 -> 'a',商=1 -> 'b'),这与常见的“位中最低位在右侧”规则一致。


 2、不用BigInteger例子

如果不用 BigInteger,那就要自己实现大数除法,直接在字符串上操作。核心思路是:

  1. 输入是一个 十进制字符串(任意长度)。

  2. 模拟除法:

    • 每次用字符串模拟 “除以 52”,得到 商字符串余数 int

    • 把余数作为当前位(base52 的一位)。

    • 把商字符串继续作为下一轮的被除数。

  3. 循环直到商为 "0"

  4. 把余数序列倒置,就是最终的 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 }

直接对上面的decimalToBase52divmod方法进行合并。代码如下:

 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     }

⚠️ 关键点说明

  1. divmod 里面要做的是 逐位除法

    • acc = remainder * 10 + 当前位

    • q = acc / base

    • remainder = acc % base

    • q 拼接进商字符串。

  2. 商可能很长,不能转成 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 = result * 52 + 当前字符的值
  • 其中 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 }

三、任何进制转换

常见的两种方法

方法一:中转十进制

这是最通用、最常见的思路。

  1. 先把 源进制(例如 base52)转成 十进制大整数(用 BigInteger 或字符串模拟)。

  2. 再把十进制数转成 目标进制(例如 base8)。

👉 适用于:任意进制之间转换

方法二:直接基数转换

如果两个进制之间有 整倍数关系(比如 2 进制 ↔ 8 进制、2 进制 ↔ 16 进制),就可以直接按位分组映射,效率更高。

  • 例子:二进制 110101 → 按 3 位一组 → 八进制 65

但是 528 没有整倍关系,所以只能用 方法一

 

posted @ 2025-09-11 15:13  Boblim  阅读(8)  评论(0)    收藏  举报