先看两个简单的数学问题:
-
一个青蛙跳跃一次的长度为
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位,实现了除法的效果。
参考
向上取整