大整数乘法---分治

分治法:

1 将问题的实例划分成同一个问题的较小的实例,最好拥有同样的规模

2 对这些较小的实例求解(一般使用递归方法,但在问题规模足够小的时候,可能会利用另一个算法)

3 如果必要的话,合并这些较小问题的解,以得到原问题的解。

自己理解看来,首先分治法重点的步骤在于合并,因为小问题求解肯定是很简单的,重点步骤在于合并小问题得到原问题的解。

大整数问题

计算公式:

a = a0a1; b = b0b1

c = a x b = c2*10^2 + c1*10 + c0

其中c1可以转化为c1 = (a1 + a0) * (b1 + b0 ) – (c2 + c0)

但是我就算知道计算公式,知道分治思想,还是写不出代码来,下面是别人的代码,必须承认写这个算法的人是个大牛:

public class BigIntMultiply
{

    //规模只要在这个范围内就可以直接计算了
    private final static int SIZE = 4;

    // 此方法要保证入参len为X、Y的长度最大值
    private static String bigIntMultiply(String X, String Y, int len)
    {
        // 最终返回结果
        String str = "";
        // 补齐X、Y,使之长度相同
        X = formatNumber(X, len);
        Y = formatNumber(Y, len);

        // 少于4位数,可直接计算
        if (len <= SIZE)
        {
            return "" + (Integer.parseInt(X) * Integer.parseInt(Y));
        }

        // 将X、Y分别对半分成两部分
        int len1 = len / 2;
        int len2 = len - len1;
        String A = X.substring(0, len1);
        String B = X.substring(len1);
        String C = Y.substring(0, len1);
        String D = Y.substring(len1);

        

        // 乘法法则,分块处理
        int lenM = Math.max(len1, len2);
        String AC = bigIntMultiply(A, C, len1);
        String AD = bigIntMultiply(A, D, lenM);
        String BC = bigIntMultiply(B, C, lenM);
        String BD = bigIntMultiply(B, D, len2);

        // 处理BD,得到原位及进位
        String[] sBD = dealString(BD, len2);

        // 处理AD+BC的和
        String ADBC = addition(AD, BC);
        // 加上BD的进位
        if (!"0".equals(sBD[1]))
        {
            ADBC = addition(ADBC, sBD[1]);
        }

        // 得到ADBC的进位
        String[] sADBC = dealString(ADBC, lenM);

        // AC加上ADBC的进位
        AC = addition(AC, sADBC[1]);

        // 最终结果
        str = AC + sADBC[0] + sBD[0];

        return str;
    }

    // 两个数字串按位加
    private static String addition(String ad, String bc)
    {
        // 返回的结果
        String str = "";

        // 两字符串长度要相同
        int lenM = Math.max(ad.length(), bc.length());
        ad = formatNumber(ad, lenM);
        bc = formatNumber(bc, lenM);

        // 按位加,进位存储在temp中
        int flag = 0;

        // 从后往前按位求和
        for (int i = lenM - 1; i >= 0; i--)
        {
            int t =
                flag + Integer.parseInt(ad.substring(i, i + 1))
                    + Integer.parseInt(bc.substring(i, i + 1));

            // 如果结果超过9,则进位当前位只保留个位数
            if (t > 9)
            {
                flag = 1;
                t = t - 10;
            }
            else
            {
                flag = 0;
            }

            // 拼接结果字符串
            str = "" + t + str;
        }
        if (flag != 0)
        {
            str = "" + flag + str;
        }
        return str;
    }

    // 处理数字串,分离出进位;
    // String数组第一个为原位数字,第二个为进位
    private static String[] dealString(String ac, int len1)
    {
        String[] str = {ac, "0"};
        if (len1 < ac.length())
        {
            int t = ac.length() - len1;
            str[0] = ac.substring(t);
            str[1] = ac.substring(0, t);
        }
        else
        {
            // 要保证结果的length与入参的len一致,少于则高位补0
            String result = str[0];
            for (int i = result.length(); i < len1; i++)
            {
                result = "0" + result;
            }
            str[0] = result;
        }
        return str;
    }

    // 乘数、被乘数位数对齐
    private static String formatNumber(String x, int len)
    {
        while (len > x.length())
        {
            x = "0" + x;
        }
        return x;
    }

    //测试桩
    public static void main(String[] args)
    {
        // 正则表达式:不以0开头的数字串
        String pat = "^[1-9]\\d*$";
        Pattern p = Pattern.compile(pat);

        // 获得乘数A
        System.out.println("请输入乘数A(不以0开头的正整数):");
        Scanner sc = new Scanner(System.in);
        String A = sc.nextLine();
        Matcher m = p.matcher(A);
        if (!m.matches())
        {
            System.out.println("数字不合法!");
            return;
        }

        // 获得乘数B
        System.out.println("请输入乘数B(不以0开头的正整数):");
        sc = new Scanner(System.in);
        String B = sc.nextLine();
        m = p.matcher(B);
        if (!m.matches())
        {
            System.out.println("数字不合法!");
            return;
        }
        System.out.println(A + " * " + B + " = "
            + bigIntMultiply(A, B, Math.max(A.length(), B.length())));
    }
}

总结来说,当自己动手写大整数相乘时,大概步骤我还是知道怎么写的,但是数位相加和将数位分离这两个函数是我没有想到的。

通过数位分离将树分离成了进位,那么可以将字符串合并。

但是这个算法有一个美中不足的地方,就是使用了4次相乘,最后的时间复杂度为M(n) = 4 ^ log 2 n = n^2

如果使用c1的计算公式,那么算法的4次乘法就可以转化为3次乘法,那么时间复杂度就可以转为为n^1.585,这就是我要完成的任务了~

    原文作者:大整数乘法问题
    原文地址: https://blog.csdn.net/ddhdzt/article/details/50506668
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞