• 取余与位运算


    在C风格语言中(比如C,C++,C# (注:排名按出生日期 ^_^)),取余运算符定义为“%”。但在很久很久以前,CPU采用如下方法计算余数(注意,该方法只对2的N次方数系有效):

    X & (2^N - 1)

    首先从求余数谈起,我们知道,计算机中存储的方式是0和1序列:

    1 0001 2^0

    2 0010 2^1

    3 0011 2^1 + 1

    4 0100 2^2

    当我们把这些数字序列左移一位的时候... 是的,你答对了,相当与数字扩大为原来的2倍,同理可知右移一位就是缩小为原来的1/2。

    那么余数在哪里?被移掉的那些位便是余数,这是无数前人的证明结果~可以自己拿笔和纸画一画就明白了。

    那么,说了这么多,为什么一个求余的%被替换为:X & (2^N - 1)?

    举例:

    9 的二进制就是1001

    8 的二进制就是1000

    显然余数是1,按照公式这么做:

    把8的二进制1000换为8-1也就是7,7的二进制表示为:0111

    1001&0111 = 0001

    答案为1与我们想的结果一致。

    我们把9换成13,过程你亲自来一遍,结果在你没算错的情况下必然和我的一样:0101

    但是注意,这种方法只是适合于求一个数除以二的N次冥才正确

    先说原理再说疑问:

    原理:

    由于除数是2^N (N为0起始递增的整数),所以隐藏条件就是,除数的2进制真身只能是如下的方式展现:

    0001,0010,0100,1000.。。。。。

    没看出来?那我们继续:

    10000,100000,1000000.。。。。

    明白了吧,当我们求余的时候,相当于除以2的N次冥,也就是相当于把数本身右移N位,但是移走的那些余数可一去不复返了。(什么?你说在CF寄存器中?嗯,这是个好注意,也许有另种一实现?!)。有什么办法保护这些即将失去的数据呢?

    我们把上面的数字减一就发现:

    0000,0001,0011,0111,01111,011111,0111111.。。。。。。

    如您所见,当和1做位运算时候,得到的结果是那个数的本身。前面例子1001&0111= 0001相当于1001右移三位得到0001,那么(13)1101右移三位还是0001,这2个0001表示的是商,两个移走的余数分别是“001”和“101”,发现特征了嚒?除以2的N次方就是要保存被除数即将被移走的低N位。

    好了,我知道上面我讲的可能不清楚,但是下面才是真正惊险的地方:

    2^3=8 然后8表示为1000 有3个零,预示着右移位数,右移位数告诉我们被除数即将被移走几位,我们如果将这几位与1按位运算不就能知道这几位到底是0还是1,也就是能完全保存到移走的数据,它就是我们期望的余数。

    嗯,我确信看完我的解释你还在晕眩之中,别管他们,该干嘛干嘛,睡完一觉之后我想我的这些话也许会被你的大脑自动解析,你最终也会明白这是怎么一回事。

    嗯,我想说,如果早起电脑都这么做的话,那么求一个非2的N次冥的取余怎么来实现呢?

  • 相关阅读:
    hdu 5366 简单递推
    hdu 5365 判断正方形
    hdu 3635 并查集
    hdu 4497 数论
    hdu5419 Victor and Toys
    hdu5426 Rikka with Game
    poj2074 Line of Sight
    hdu5425 Rikka with Tree II
    hdu5424 Rikka with Graph II
    poj1009 Edge Detection
  • 原文地址:https://www.cnblogs.com/littleKing163/p/5695153.html
Copyright © 2020-2023  润新知