取模操作(补充说明)

来源:互联网 发布:投稿软件 编辑:程序博客网 时间:2024/05/16 13:51

起源

在算法中,有时候要用到取模操作,为的是避免数据过大溢出,而有时候只需要验证算法的有效性,并关心实际的值,下面给出取模操作的性质以及一个简单的应用例子。

模运算性质

  1. ( a + b ) % c = ( ( a % c ) + ( b % c ) ) % c
  2. ( a * b ) % c = ( ( a % c ) * ( b % c ) ) % c
  3. ( a – b ) % c = ( ( a % c ) – ( b % c ) ) % c
  4. ( a / b ) % c NOT EQUAL TO ( ( a % c ) / ( b % c ) ) % c
  5. (a / b ) % c = ( a * b^ -1 ) % c

    the answer ( a % b) always be less than b.

基本上是要记住上面这几条的,基本够用。。而b经常取的数为一个比较大的素数

为什么需要模运算性质

很显然,有的时候a + b的值越界(超过Integer.MAX_VALUE),有的时候a * b的值越界(超过Integer.MAX_VALUE)。所以需要相关的模运算性质,在越界之前及时处理。
最频繁的公式:

1.( a + b ) % c = ( ( a % c ) + ( b % c ) ) % c
2.( a * b ) % c = ( ( a % c ) * ( b % c ) ) % c

数据的范围

int的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1)
long的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1)

实际应用

取模运算用的最多的地方是哈希函数的实现,以及DP用于优化空间的滚动数组,再就是一些数论算法,以及避免中间过程的溢出。

阶乘程序

    public static long  factorial1(int n){        long fact = 1;        for (int i = 1; i <= n; i++){            fact = fact * i;        }        return fact;    }    public static long  factorial2(int n){        long fact = 1;        int M = 1000000007;        for (int i = 1; i <= n; i++){            fact = (fact * i) % M;        }        return fact;    }    public static long  factorial3(int n){        long fact = 1;        int M = 1000000007;        for (int i = 1; i <= n; i++){            fact = fact * i;  //WRONG APPROACH        }        return fact % M;    }

这三个阶乘程序f1 f2 f3 分别代表不用模操作,正确使用模操作, 错误使用模操作。。。
f1和f3的程序会溢出,f3虽然使用模操作,但是是错误的示范,模操作应该应用到中间计算过程中。。
这里f2的M选了一个很大的素数,这样可以保证程序在小规模的n能够输出正确结果,同时又保证了大规模的n程序不会溢出出错。

参考:https://www.quora.com/What-exactly-is-print-it-modulo-10-9-+-7-in-competitive-programming-websites

1 0
原创粉丝点击