算法导论实践——大数乘法(二进制)

算法导论实践——大数乘法(二进制)

感觉算法思路很简单,却实现了一个下午,可能还是对代码理解得不够深刻吧,很多地方感觉很冗余,不过好歹实现了,在此记录一下;

理论

在这里插入图片描述
算法的核心是把大数乘法的时间复杂度降到O(nlog3)。在上图中,有递归方程:

在这里插入图片描述
其中,Theta(n)是2n次方乘积用移位方式实现的结果。可以看出,子问题主要是由不同项乘积带来的,所以,重点是处理乘法项。这里将中间的和式写为:
在这里插入图片描述
则递归方程就变成了
在这里插入图片描述
OK,开始实现。

C#实现

BigIntegerMultiplicationUtil

using System;
using System.Collections.Generic;
using System.Text;

namespace BigInteger
{
    public static class BigIntegerMultiplicationUtil
    {
        public static int[] X;	//二进制数组X
        public static int[] Y;	//二进制数组Y
        private static string RawX;//用户输入的X
        private static string RawY;

        private static int MaxLength;//乘积应该开辟的数组长度

        public static void InputInteger()
        {
            
            Console.WriteLine("输入X:");
            RawX = Console.ReadLine();
            Console.WriteLine("输入Y:");
            RawY = Console.ReadLine();
            X = new int[RawX.Length];
            Y = new int[RawY.Length];
            RawToBinary(RawX, X);
            RawToBinary(RawY, Y);
            
            MaxLength = X.Length * Y.Length;
        }
        
		//将用户输入的数组raw,转换为二进制数组binary
        private static void RawToBinary(string raw,int[] binary)
        {
            for(int i = 0; i < raw.Length; i++)
            {
                binary[i] = raw[i] - '0';
            }
        }

        public static int[] BigIntegerMultiplication(int[] x, int[] y)
        {
        	//获取二进制数组的有效位数(比如00001的有效位数为1,其中,默认00000的有效位数为1)
            int xLength = GetEffctiveLength(x);
            int yLength = GetEffctiveLength(y);
            //获取二进制数组有效位数开始的下标,比如00001的Start为4,其中默认00000的Start为0
            int xStart = GetStartWith(x) == -1 ? 0 : GetStartWith(x);
            int yStart = GetStartWith(y) == -1 ? 0 : GetStartWith(y);
			
			//递归终止条件,即,若某个数组的有效长度为1,就可以计算了
            if (xLength == 1)
            {
                return SimpleMulti(y, x[^1]);
            }
            if (yLength == 1)
            {
                return SimpleMulti(x, y[^1]);
            }

			//**分解过程,n代表数组的中点下标**//
			//假设x为10000,则n1 = 5/2 = 2 ;y为0010,则n2 = 2/2 = 1
            int n1 = xLength / 2;
            int n2 = yLength / 2;
			
			//获取子数组,还是以x为10000,y为0010例,由于MaxLength = 5 * 4 = 20,所以
			//A = 0000 0000 0000 0000 0010(20位)
			//B = 0000 0000 0000 0000 0000(20位)
			//(这里其实做的很瓜皮,根本没必要这么搞(指把这里面出现的所有数组都统一长度),可是当时想着偷懒,不用算长度了,没想到后面却搞出一堆问题,哎,说明我得层数还不够啊,以后有时间再进行优化,今天懒得搞了,等等搞Xamarin去了)
            int[] A = SubIntArray(x, xStart, xStart + n1 - 1);
            int[] B = SubIntArray(x, xStart + n1, x.Length - 1);
            
			//C = 0000 0000 0000 0000 0001(20位)
			//D = 0000 0000 0000 0000 0000(20位)
            int[] C = SubIntArray(y, yStart, yStart + n2 - 1);
            int[] D = SubIntArray(y, yStart + n2, y.Length - 1);

            /*演算步骤(这里考虑一般性):
            (x = A * 2^n1 + B, y = C * 2^n2 + D
            xy = ac*2(n1+n2)+bd+ad*2^n1+bc*2^n2
            xy=ac*2(n1 + n2) + bd + (ad+bc)*2^n1 + bc*(2^n2-2^n1)
            xy=ac*2^(n1 + n2) + bd + ((a+b)(c+d) - ac - bd)*2^n1 + bc*(2^n2-2^n1)
            xy = ac*2^(n1 + n2) + bd + (a+b)(c+d) * 2^n1 - ac*2^n1 - bd*2^n1 + bc*(2^n2-2^n1))*/
			
			//**递归求解过程,演算上面的表达式**//
            int[] AC = BigIntegerMultiplication(A, C);
            int[] BD = BigIntegerMultiplication(B, D);
            int[] BC = BigIntegerMultiplication(B, C);

            int[] AaddB = BinaryAdd(A, B);
            int[] CaddD = BinaryAdd(C, D);

            int[] AaddBCaddD = BigIntegerMultiplication(AaddB, CaddD);
			
			//处理奇数问题,这里我的A = 0000 0000 0000 0000 0010
			//事实上,此时的x = A * 2^n1[向上取整] + B,大家可以自己推演一下,结合前面部分的理论的那张图,就知道了。
            n1 = xLength % 2 == 1 ? xLength / 2 + 1 : xLength / 2;
            n2 = yLength % 2 == 1 ? yLength / 2 + 1 : yLength / 2;
			//一顿演算
            int[] AddPart = BinaryAdd(BinaryAdd(BinaryAdd(MoveLeft(AC, (n1 + n2)), BD), MoveLeft(AaddBCaddD, n1)), MoveLeft(BC, n2));
            int[] XY = BinarySub(BinarySub(BinarySub(AddPart, MoveLeft(AC, n1)), MoveLeft(BD, n1)),MoveLeft(BC,n1));
			
			//返回结果
            return XY;
        }
		
		//打印结果
        public static void GetResult(int[] A)
        {
            int startWith = -1;
            for(int i = 0; i < A.Length; i++)
            {
                if (A[i] != 0)
                {
                    startWith = i;
                    break;
                }
            }
            if(startWith == -1)
            {
                Console.Write("0");
            }
            else
            {
                for(int i = startWith; i < A.Length; i++)
                {
                    Console.Write(A[i]);
                    Console.Write(" ");
                }
            }
            Console.WriteLine(" ");
        }
		
		//获取二进制数组的有效长度
        public static int GetEffctiveLength(int[] A)
        {
            int startWith = -1;
            for (int i = 0; i < A.Length; i++)
            {
                if (A[i] != 0)
                {
                    startWith = i;
                    break;
                }
            }
            if (startWith == -1)
            {
                return 1;
            }
            else
            {
                return A.Length - startWith;
            }
        }
		
		//获取二进制数组有效长度的开始的下标
        public static int GetStartWith(int[] A)
        {
            int startWith = -1;
            for (int i = 0; i < A.Length; i++)
            {
                if (A[i] != 0)
                {
                    startWith = i;
                    break;
                }
            }
            return startWith;
        }
			
		//数组左移a位
        public static int[] MoveLeft(int[] A, int a)
        {
            int[] B = new int[MaxLength];
			//获得有效数组,因为前面我的ABCD长度都直接设置为MaxLength了,但是这就很蠢了,根本就没有必要---
            int[] EffectiveA = SubIntArray(A, A.Length - GetEffctiveLength(A), A.Length - 1, true);
            for (int i = EffectiveA.Length - 1; i >= 0; i--)
            {
                B[MaxLength - a + i - EffectiveA.Length] = EffectiveA[i];
            }
            return B;
        }
        
		//二进制加法
		//这算是统一长度之后的好处吧,就是加法特别简单
        public static int[] BinaryAdd(int[]A,int[] B)
        {

            for(int i = MaxLength - 1; i >= 0; i--)
            {
                if(A[i] + B[i] == 2)
                {
                    A[i] = 0;
                    A[i - 1]++; 
                }

                else if(A[i] + B[i] == 3)
                {
                    A[i] = 1;
                    A[i - 1]++;
                }

                else
                {
                    A[i] += B[i];
                }
            }
            return A;
        }
        
		//二进制减法
		//和加法类似
        public static int[] BinarySub(int[] A, int[] B)
        {
            for (int i = MaxLength - 1; i >= 0; i--)
            {
                if (A[i] - B[i] == -1)
                {
                    A[i] = 1;
                    A[i - 1]--;
                }
                else if(A[i] - B[i] == -2)
                {
                    A[i] = 0;
                    A[i - 1]--;
                }
                else
                {
                    A[i] -= B[i];
                }
            }
            return A;
        }

		//获取统一长度的子数组
        public static int[] SubIntArray(int[] A, int p, int q)
        {
            int[] B = new int[MaxLength];
            for (int i = q; i >= p; i--)
            {
                B[MaxLength - 1 + (i - q)] = A[i];
            }       
            return B;
        }
        
		//重载SubIntArray,获取有效长度的子数组
		//大概只用到了一个地方(左移MoveLeft的时候)
        public static int[] SubIntArray(int[] A, int p, int q, bool isCutTrue)
        {
            int[] B = new int[GetEffctiveLength(A)];
            if(GetEffctiveLength(A) == 1)
            {
                B[^1] = A[^1];
            }
            else
            {
                for (int i = q; i >= p; i--)
                {
                    B[GetEffctiveLength(A) + (i - q) - 1] = A[i];
                }
            }
            return B;
        }

		//计算朴素乘法,事实上a的值只可能为0或者1,因此,直接判断就好了
        public static int[] SimpleMulti(int[] A, int a)
        {
            int[] B = new int[MaxLength];
            if (a == 0)
            {
                for (int i = 0; i < A.Length; i++)
                {
                    B[MaxLength - i - 1] = 0;
                }               
            }
            else
            {
                for (int i = 0; i < A.Length; i++)
                {
                    B[MaxLength - i - 1] = A[A.Length - i - 1];
                }
            }
            return B;
        }

    }
}

Program

using System;
using BigInteger;

namespace BigInteger
{
    class Program
    {
        static void Main(string[] args)
        {
            //Console.WriteLine("Hello World!");
            BigIntegerMultiplicationUtil.InputInteger();
            int[] X = BigIntegerMultiplicationUtil.X;
            int[] Y = BigIntegerMultiplicationUtil.Y;
            int[] XY = BigIntegerMultiplicationUtil.BigIntegerMultiplication(X, Y);
            BigIntegerMultiplicationUtil.GetResult(XY);
            
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值