5.3 - 求模和指数运算
Key Takeaway
- 在需要判断一个数是否能被另外一个数整除(除法结果没有余数)时求模运算非常有:如果
x%y
结果为0,说明x
可以被y
整除。 - 负数也可以求模。
x % y
的结果总是和 x 的符号一样。 - C++ 不支持指数运算符,可以使用
<cmath>
中的pow
函数 - 编写整型指数运算函数可以使用平方求幂法
求模运算符
求模运算符(有时候也被叫做求余运算符)可以返回整数除法结果的余数。例如 7 / 4 = 1 余 3。因此 7 % 4 = 3。又比如,25 / 7 = 3 余 4,因此 25 % 7 = 4。求模运算只能用于整数操作数。
在需要判断一个数是否能被另外一个数整除(除法结果没有余数)时求模运算非常有:如果 x%y
结果为0,说明 x
可以被 y
整除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
程序运行结果如下:
1 2 3 4 |
|
1 2 3 4 |
|
接下来,我们实时如果第二个数比第一个数大会怎么样:
1 2 3 4 |
|
余数是 2 ,乍一看原因可能不是很明显,但其实很简单:2 / 4 是 0 (整数除法) 余数为 2。任何时候只要第二个数大于第一个,则第一个数为余数。
负数求模
负数也可以求模。x % y
的结果总是和 x 的符号一样。
执行上述程序:
1 2 3 4 |
|
1 2 3 4 |
|
在上面两种例子中可以看到余数的符号总是和第一个操作符一样。
指数运算符去哪了?
你可能已经注意到了 ^
运算符(通常用于表示指数运算)在 C++ 中是异或运算符 (在O.3 -- Bit manipulation with bitwise operators and bit masks 中介绍)。C++ 并没有指数运算符。
在 C++ 中进行指数运算需要 #include
<cmath>
头文件,然后使用 pow() 函数:
1 2 3 |
|
注意,参数和返回值的类型都是 double。而由于浮点数存在舍入误差,所以pow()
的结果可能并不精确(即使你传入的是整数)。
如果你想进行整数指数运算,你最好自己写一个函数。下面这个函数就实现了整型的指数运算(使用了不太直观的平方求幂算法以提高效率):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
译者注
求x的n次方,最直接的算法是把x乘以x执行n次,但是这样很慢。 使用平方求幂的方法可以极大提高速度,它的原理如下:
- 假设求x的16次方,其实没必要把x乘以x执行16次
- 更快的方法是把x平方,然后再平方,再平方,再平方。即\(((((x^2)^2)^2)^2)\),因为我们知道求指数的指数实际上是把两个指数相乘。所以\(((((x^2)^2)^2)^2)=x^{16}\)
- 如果转换为更一般的形式,x的n次方其实就是看n转换成2进制之后,里面包含多少个1。假设n = 37,则转换为二进制是
100101
。 - \(x^{37} = x^1 * x^{100} * x^{10000}\), 即\(x^{37} = x^1 * x^{4} * x^{32}\)
- 所以,我们只要把指数先转变成二进制,然后一位一位的看,如果遇到1,则看该位是第几位,然后求2的几次方,假设结果为n,则再次求x的n次方,最后把所有的结果乘起来。
- 不过按照上面计算每个部分的时候还是很麻烦,实际可以这样做:
- 不论是1还是0,都把x自身成以自身
- 如果遇到1,则把x的结果乘到result上
- 如果遇到0,跳过
- 最后的结果相当于每处理指数的一个位,x都平方一次。然后只有a位上为1时,x的a次方才被乘到结果上,这样就很自然,不用再特意去计算当前是第几位了。
结果为:
1 |
|
如果你不理解这个函数的原理也不要紧 —— 使用它并不需要你了解它的原理。
注意
大部分情况下,整型的指数运算都可能会溢出整型。这很可能是该函数没有被包含到标准库中的原因。