先看两个简单的数学问题:
-
一个青蛙跳跃一次的长度为
3,现在它垂直于马路方向要跳跃整条马路。假定马路的宽为x长? 问:它最少需要跳跃几次能够完全跳过这条马路? -
一个房间可以住
6个人,现在来了一群人,人数为x,问最少需要多少个房间才能让这x个人都可以住下? 假设一个房间住满的情况下,才安排另外一个房间。
问题分析:
假设马路的长度是10,青蛙一次跳3,跳3次的距离是9 ,4次的距离是12,为了能完全跳过马路,需要跳跃4次
10/3 + 1 = 4
如果马路的长度是12,为了能完全跳过马路,需要跳跃4次:
12/3 = 4
如果马路的长度是13,需要5次才能完全过去:
13/3 + 1 = 5
所以,这两个题目, 都是同样的道理,不难得出结论:
result = (x%n == 0) ? (x/n) : (x/n + 1)
三目运算可以解决此类问题,但针对此类问题有一个较为巧妙的算法公式, 分子是总量加上步长减去1 ,运算后的结果再去除以步长:
result =(x+n-1)/ n
计算机中的整数运算是向下取整的,也就是如果能整除,没有余数,那结果正好;如果不能整除,有余数,就将余数舍弃,保留相除后的整数部分。但此时正好和我们想要的结果(向上取整)相差一,正确的结果需要加上这个一。
下面是对这个公式的分析:
-
因为
x/n的余数(计为z)总小于n,这样就能保证,余数(z + n-1) < 2n,所以z + (n-1)/n的值不是0就是1,如果z == 0,z + (n-1)/n = 0,如果z > 0,z + (n-1)/n = 1。 -
所以:
result =(x+n-1)/ n,就能保证:如果x+n-1的余数等于n-1,x/n刚好能够整除余数z是0,向上取整不需要加1,如果x+n-1的余数大于n-1,那么x/n不能整除,有余数(z),向上取整就需要x/n + 1,这里的1就是(z+n-1)/n = 1。
所以在C语言中,ceil函数可以使用以下公式:
int ceil(int x, int y) {return (x + y - 1) / y;
}
这个公式的原理和上面提到的一样,当x除以y时,如果x不能被y整除,那么向上取整后的结果应该是x除以y的商加上 1。
结合移位运算-实例分析
假设收到比特流157位,利用位运算如何得出占据多少个字节。
157 + 7 >> 3得到20字节:
// 157 +7 >> 3的二进制过程为:(用8位表示) 10011101
+ 111
--------------10100000>>3
--------------00010100=20
对157/8向上取整的做法:157除以8,如果有余数就对结果+1。
这里位运算的思想是:除以8,在位运算中就是右移3位,丢掉最后的3位。因为是向上取整,所以如果最后3位中只要有一个1,就不能单纯地丢掉这3位。都需要将第四位+1,表现出来的形式就是+7。
看一下7的二进制表示就很明显了,7的二进制是0b111,如果某个数的最后3位只要有一个1,再加上7,那么最终都会进位导致这个数的最后第四位+1,实现了向上取整的效果。然后再右移3位,实现了除法的效果。
参考
向上取整