Detailed explanation of large number division (super long integer operation divider)

In the operation of large numbers, the more difficult to implement should be the high-precision / high-precision divider.

catalogue

1, Principle

2, Specific code analysis

3, Super long integer operation

1, Principle

1. Mass storage

         First, let's talk about how large numbers are stored in C language programs. We use an int array of length N to store unsigned super long integers, where the upper limit of the value stored in each element of the array is M. As follows:        

#define M 10000 	// M-ary, maximum value of each element of int array
#define N 5 		// Array length
int x[N]={0};

          Because the int type represents at most 10 significant digits (the maximum value is 2147483648), when two int type variables with significant digits of 5 are multiplied, the result may overflow. In order to facilitate the implementation of the multiplier, the value of M should be less than 10000  

         For example, when we want to store an unsigned super long integer 6543210435135314 with X, the value of array x is as follows: x[0]=5314,x[1]=3513,x[2]=2104,x[3]=6543,x[4]=0.

2. Division algorithm

The relationship between division operands is as follows:

         Divisor / divisor = quotient... Remainder, where divisor = quotient * divisor + remainder

First, the divisor cannot be 0;

When the divisor is 0, the quotient is 0;

When the divisor is less than the divisor, the quotient is 0 and the remainder is the divisor;

When the divisor is equal to the divisor, the quotient is 1 and the remainder is 0;

 ......

1) In daily life, we calculate division like this:

 

2) But in the division of large numbers, we can't do it directly. For example, for 1234 5678 9000 / 1 2345, the computer cannot directly calculate the result of 1234 5678 / 1 2345.

3) In the computer, the implementation of subtraction is much simpler than division, so we consider transforming division into subtraction. For example, for 7894 / 32, we have:

 

By constantly subtracting the divisor from the divisor, we get the remainder 22, and the number of times the divisor is reduced is quotient 246.  

4) Now we know how to convert division into subtraction in the computer, but the subtraction of divisor by divisor is too slow. Is there a faster algorithm? The answer is yes. We can speed up the "subtraction" by shifting, for example:

 

  As shown in the figure, 7894 = 32 * 2 * 100 + 32 * 4 * 10 + 32 * 6 * 1 + 22, a total of 2 * 100 + 4 * 10 + 6 * 1 = 246 32 are subtracted, so the quotient of 7894 / 32 is 246.

2, Specific code analysis

1. First, the function prototype is as follows:

#define M 10000 	// M-ary, maximum value of each element of int array
#define N 5 		// Array length
int div2(int n1[],int n2[],int quotient[]);
/*	Large number division: unsigned high-precision integer / unsigned high-precision integer. n1 is the divisor, n2 is the divisor, and quotient is the quotient. The function returns the integer 1 when executed correctly	*/

These functions are also involved:  

int add(int num1[],int num2[],int sum[]);	
/* Large number addition */ 
int sub(int num1[],int num2[],int difference[]);
/* Large number subtraction */ 
int mul(int num1[],int num2[],int product[]);
/* Large number multiplication */
int div(int num1[],int num2,int quotient[]);
/* Large number division: unsigned high precision integer / unsigned low precision integer, where num2 > 0 and num2 < = M */ 
int cmp(int num1[],int num2[]);	
/* For large number comparison, return 1 when num1 > num2, return - 1 when it is less than, and return 0 when it is equal to */ 

  2. Abnormal value handling

         Because we store unsigned super long integers here, the operands involved in the operation should also be unsigned, and each element of the array should be less than M. So there are:  

int div2(int n1[],int n2[],int quotient[]){
int i=0;
int num1[N]={0},num2[N]={0};
	for(i=0;i<N;i++){
//Outlier handling
		if(n1[i]<0||n2[i]<0||n1[i]>=M||n2[i]>=M){
			printf("div2():Operand are forbidden!\n");
			return -2;
		}
//initialization:
       num1[i]=n1[i];
		num2[i]=n2[i];
		quotient[i]=0;
}
return 1;//Correct operation
}

         Here we explain why the function does not directly use n1 and n2 to participate in the operation, because the parameters passed by this function div2 are pointers, and the next algorithm will change the value of the divisor. In order to prevent the value of the divisor from changing after the end of the function, two local variables num1 and num2 equal to the values of n1 and n2 are newly defined.

3. Special value treatment

         It includes the handling of the following special cases:   (1) First, the divisor cannot be 0; (2) When the divisor is 0, the quotient is 0; (3) When the divisor is less than the divisor, the quotient is 0; (4) When the divisor is equal to the divisor, the quotient is 1.

 /*----Special value processing----*/
    //Check whether the divisor is 0
    for(i=N-1;i>=0&&(num2[i]==0);i--);//Search num2 for the first subscript that is not 0 from the high order
    if(i<0){
        printf("Divisor cannot be 0\n");
        return -1;
    }
    //Divisor less than divisor
    if((flag=cmp(num1,num2))==-1){
        quotient[0]=0;
        return 1;
    }
    //The divisor is equal to the divisor
    else if(flag==0){
        quotient[0]=1;
        return 1;
    }
/*---Special value processing end---*/

 // Don't forget to declare flag in front!

4. After handling the above special cases, the rest is the case where the divisor is greater than the divisor. From here on, it is the core part of the algorithm.

         First, in order to speed up the deceleration, we should move the divisor to the left as much as possible. Since the value of M is 10000 and the significant digits of num2[i] are 4, we first shift the 4 bits (decimal bits) to the left until the divisor num2 is greater than the divisor num1. Next, move the divisor 1 bit (decimal bit) and 1 bit (decimal bit) to the right until num1 > num2. At this time, the divisor num1 is just greater than the divisor num2.  

/*---Calculate the maximum shift of divisor---*/
    if(num2[N-1]>0)
        flag=0;//flag=0 indicates that the divisor cannot be shifted because the highest bit is > 0

    for(counts=0;counts<N&&(flag>0);){
        //num2 shift left 4 bits:
        for(i=N-1;i>0;i--)
            num2[i]=num2[i-1];
        num2[0]=0;
        counts++;//Shift left times + 4, counts+1
        flag=cmp(num1,num2);
    }
    counts=counts*4;
    for(i=0;i<counts&&(cmp(num1,num2)<0);i++){
        if(div(num2,10,num2)!=1) return -1;//If the divisor num2 is greater than the dividend num1, shift it by 1 bit to the right
    }
    counts=counts-i;//At this time, counts is the left shift of divisor
/*--Calculate the maximum shift of divisor end--*/

//Here, the left shift of one decimal bit is realized by high-precision integer / low-precision integer division div

//counts is num2 left shift digit (decimal digit). Don't forget to declare it in the front!

5. Calculate the number of times the divisor is subtracted from the divisor

for(;counts>=0;counts--){
    if(cmp(num1,num2)>=0){
        //When the divisor num2 shifts the counts bit to the left, num1 can subtract num2 at most times:
        for(times=0;times<M&&(cmp(num1,num2)>=0);times++){
            sub(num1,num2,num1);
			}
        //When the divisor num2 shifts the counts bit to the left, the divisor t=times*(10^counts) can be subtracted from the dividend at most:
		for(i=0;i<N;i++)
		{t[i]=0;m[i]=0;}//Initialize t, m
		t[0]=times;
		m[0]=10;
		for(i=0;i<counts;i++){
			if(mul(t,m,t)!=1)
				return -1;
		}
        //Number of "minus":
		if(add(quotient,t,quotient)!=1) return -1;
	}
    if(div(num2,10,num2)!=1) return -1;//Divisor shift right 1 bit
}

 // Don't forget to declare times, t, m in front!

6. The complete div2 function is defined as follows:

#define M 10000 	// M-ary, maximum value of each element of int array
#define N 5 		// Array length
//Function declaration:
int add(int num1[],int num2[],int sum[]);
int cmp(int num1[],int num2[]);
int sub(int num1[],int num2[],int difference[]);
int mul(int num1[],int num2[],int product[]);
int div(int num1[],int num2,int quotient[]);
/*
===Unsigned super long integer division===
quotient=n1/n2
 High precision integer / high precision integer
*/
int div2(int n1[],int n2[],int quotient[]){
    int i=0,flag=1,counts=0,times=0;
	int t[N]={0},m[N]={0},num1[N]={0},num2[N]={0};
	for(i=0;i<N;i++){
        //Outlier handling
		if(n1[i]<0||n2[i]<0||n1[i]>=M||n2[i]>=M){
			printf("div2():Operand are forbidden!\n");
			return -2;
		}
        //initialization:
        num1[i]=n1[i];
		num2[i]=n2[i];
		quotient[i]=0;
    }

    /*----Special value processing----*/
    //Check whether the divisor is 0
    for(i=N-1;i>=0&&(num2[i]==0);i--);//Search num2 for the first subscript that is not 0 from the high order
    if(i<0){
        printf("Divisor cannot be 0\n");
        return -1;
    }
    //Divisor less than divisor
    if((flag=cmp(num1,num2))==-1){
        quotient[0]=0;
        return 1;
    }
    //The divisor is equal to the divisor
    else if(flag==0){
        quotient[0]=1;
        return 1;
    }
    /*---Special value processing end---*/

    /*---Calculate the maximum shift of divisor---*/
    if(num2[N-1]>0)
        flag=0;//flag=0 indicates that the divisor cannot be shifted because the highest bit is > 0
    for(counts=0;counts<N&&(flag>0);){
        //num2 shift left 4 bits:
        for(i=N-1;i>0;i--)
            num2[i]=num2[i-1];
        num2[0]=0;
        counts++;//Shift left times + 4, counts+1
        flag=cmp(num1,num2);
    }
    counts=counts*4;
    for(i=0;i<counts&&(cmp(num1,num2)<0);i++){
        if(div(num2,10,num2)!=1) return -1;//If the divisor num2 is greater than the dividend num1, shift it by 1 bit to the right
    }
    counts=counts-i;//At this time, counts is the left shift of divisor
    /*--Calculate the maximum shift of divisor end--*/

    for(;counts>=0;counts--){
        if(cmp(num1,num2)>=0){
            //When the divisor num2 shifts the counts bit to the left, num1 can subtract num2 at most times:
            for(times=0;times<M&&(cmp(num1,num2)>=0);times++){
                sub(num1,num2,num1);
            }
            //When the divisor num2 shifts the counts bit to the left, the divisor t=times*(10^counts) can be subtracted from the dividend at most:
            for(i=0;i<N;i++)
            {t[i]=0;m[i]=0;}//Initialize t, m
            t[0]=times;
            m[0]=10;
            for(i=0;i<counts;i++){
                if(mul(t,m,t)!=1)
                    return -1;
            }
            //Number of "minus":
            if(add(quotient,t,quotient)!=1) return -1;
        }
        if(div(num2,10,num2)!=1) return -1;//Divisor shift right 1 bit
    }
    return 1;//Correct operation
}

3, Super long integer operation

        If you need the complete code of large number operation (including input and output, addition, subtraction, multiplication and division functions), you can refer to another article: Super long integer operation -- starting from Fibonacci sequence   The < unsigned super long integer operation > part of.

Tags: C Algorithm

Posted on Mon, 08 Nov 2021 16:20:04 -0500 by mwichmann4