如何避免模乘的溢出?

时间:2015-02-01 05:35:12

标签: c math overflow modular-arithmetic

我知道,

(a*b)%m = ((a%m)*(b%m))%m

但有可能出现溢出。 为简单起见,假设整数大小为2位。 如果a = 2(即10 2 )且b = 2(即10 2 ),则m = 3(即11 2 ),则 a%m和b%m结果为2,并且在乘法之后,答案是4(即100),其不适合整数大小。如果从4开始考虑2-lsb,最终答案将为0。 但实际答案是1。

我应该怎样做以避免这种情况?

2 个答案:

答案 0 :(得分:4)

如果m-1平方不适合您的整数类型,则需要执行长乘法。对于您的两位示例,这意味着将您的两位数字分成一位数字(高位和低位)并将所有四对相乘(高高,高低,低高,低)单独地。然后,您可以为每对得到结果mod m(注意它们代表的实际位置,即四个,两个或一个)并添加结果mod m

答案 1 :(得分:1)

许多小型处理器C实现可以直接检查上溢/下溢的数学运算结果。

另一种方法是使用两倍于底层int大小I.E的接收字段。对于int大小为2,使用4字节的结果字段。 (也许通过long long int)或将两个数字都传递给double字段并将它们相乘然后转换回int(但是,结果中的某些精度(I.E.最低有效数字)可能不准确。

另一种方法是使用math.h库中的适当函数。

另一种方法是使用数组使用长乘法: 这是从http://www.cquestions.com/2010/08/multiplication-of-large-numbers-in-c.html

复制而来的
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#define MAX 10000

char * multiply(char [],char[]);
int main(){
    char a[MAX];
    char b[MAX];
    char *c;
    int la,lb;
    int i;
    printf("Enter the first number : ");
    scanf("%s",a);
    printf("Enter the second number : ");
    scanf("%s",b);
    printf("Multiplication of two numbers : ");
    c = multiply(a,b);
    printf("%s",c);
    return 0;
}

char * multiply(char a[],char b[]){
    static char mul[MAX];
    char c[MAX];
    char temp[MAX];
    int la,lb;
    int i,j,k=0,x=0,y;
    long int r=0;
    long sum = 0;
    la=strlen(a)-1;
    lb=strlen(b)-1;

    for(i=0;i<=la;i++){
            a[i] = a[i] - 48;
    }

    for(i=0;i<=lb;i++){
            b[i] = b[i] - 48;
    }

    for(i=lb;i>=0;i--){
        r=0;
        for(j=la;j>=0;j--){
            temp[k++] = (b[i]*a[j] + r)%10;
            r = (b[i]*a[j]+r)/10;
        }
        temp[k++] = r;
        x++;
        for(y = 0;y<x;y++){
            temp[k++] = 0;
        }
   }

   k=0;
   r=0;
   for(i=0;i<la+lb+2;i++){
        sum =0;
        y=0;
        for(j=1;j<=lb+1;j++){
            if(i <= la+j){
                sum = sum + temp[y+i];
            }
            y += j + la + 1;
        }
        c[k++] = (sum+r) %10;
        r = (sum+r)/10;
   }
   c[k] = r;
   j=0;
   for(i=k-1;i>=0;i--){
      mul[j++]=c[i] + 48;
   }
   mul[j]='\0';
   return mul;

}

上述代码的示例输出:

输入第一个数字:55555555

输入第二个数字:3333333333

两个数字的乘法:

185185183314814815

大数乘法的逻辑

正如我们在c中所知,没有任何这样的数据类型可以存储非常大的数字。例如,我们想要解决表达式:

55555555 * 3333333333

上述表达式的结果是非常大的数字,超出了甚至长整数或长双精度的范围。那么问题是如何在c中存储如此大的数字?

解决方案非常简单,即使用数组。上面的程序使用了相同的逻辑,我们使用它作为通常的逻辑来乘以两个数字,而不是将数据存储在我们存储到数组中的正常变量中。