• 取余操作的原理


    虽然以前学过,但是已经忘了,在返秦的火车上看到如此问题,于是思考了一下。


    C是高级语言,a-(a整除b)*b就不用说了,“整除”是怎么来的?
    但相对于二进制运算,汇编也算一种“高级”语言,那么我问,“div”是怎么来的?

    来看一个例子,如果除数的数是2的幂次的话:

    • 除数2,二进制为0000 0010,被除数5,二进制为0000 0101。除数-1,即此时除数变为0000 0001,两者进行与操作,结果为0000 0001,余数为1。
    • 除数4,二进制为0000 0100,被除数13,二进制为0000 1101。除数-1,即此时除数变为0000 0011,两者进行与操作,结果为0000 0001,余数为1。
    • 除数8,二进制为0000 1000,被除数47,二进制为0010 1111。除数-1,即此时除数变为0000 0111,两者进行与操作,结果为0000 0111,余数为7。

    原理明白了吧,但这种方法仅仅适用于除数为2的幂次数的时候。
    另,这种方法会出现在位图法的位向量操作中,看着是与操作,实际上就是取余。

    #define MAX 1000000  
    #define SHIFT 5  
    #define MASK 0x1F  
    #define DIGITS 32  
      
    int a[1+MAX/DIGITS];
    //置指定位为1
    void setbit(int n) 
    { 
     a[n>>SHIFT] |= (1<<(n&MASK)); 
    }
    //清空指定位                                              
    void clearbit(int n)  
    {  
     a[n>>SHIFT] &= ~(1<<(n&MASK));  
    }
    //检测指定位是否已设置为1    
    int test(int n)  
    {  
        return a[n>>SHIFT] & (1<<(n&MASK)); 
    } 
    

    再次言归正传。
    取余是通过电路进行二进制除法来实现的,简略的原理图(具体的要复杂很多)我记得在数电的教科书上有,改天我查到了补上。
    那么二进制除法是怎样的呢?其实网上一搜一堆原理。(TAT我在高铁上还以为是我自创来着)
    (我们可以直接用减法来实现,但这样的话,如果是10324除以3,就会循环非常多次。)
    针对这种情况,我的想法是:
    1,将除数左移,至与被除数的最高位对齐或者小一位。(小一位是因为待会得用被除数减去左移后的除数,这个时候被除数肯定>(除数*某个2的n次))
    2,被除数减去左移后的除数。
    3,除数左移,这个时候,被除数肯定>(除数*某个2的n1次)
    。。。。
    不停迭代,直至被除数<除数,这个时候被除数就是余数。
    我们先来看不考虑商,仅仅考虑取余

    我们随便找两个数,假设是2381除以7。
    2381,二进制为1001 0100 1101
    7,二进制为111
    用2381的最高位1001减去111,得10。相当于2381-7*256 = 589 (这个缩小得很快的,所以其实除非除数和被除数非常大,或者超出机器位数,每次操作要多读几遍,肯定会有性能损失)

    589 = 刚才的答案10+之前没用过的右边01001101 = 1001001101
    再次重复刚才的过程
    589的最高位1001减去111,得10。相当于589-7*64 = 141

    141 = 刚才的答案10+之前没用过的右边001101 = 10001101
    。。。。。
    如此循环,最后,当被除数剩1时即为余数

    那么商跑到哪里去了呢?
    请看二进制除法原理,原理和我这个是一样的,只不过必须控制移位
    继续用刚才的例子,2381除以7
    以下是不停计算的过程,{}里的是每一次计算的答案,()里的是每一次没参加计算的位

       1001 (0100 1101)             //2381
      -0111                        //7
    --------------------------
      {0010}(0100 1101)          //此时0010>0,所以商的最高位为1
      -0011  1                  //这一次不够减,所以商的次高位为0
    除数继续右移
       0010 01(00 1101)
      -0001 11
    ----------------------------
      {0000 10}(00 1101)   //此时000010>0,所以商的第3高位为1
      -0000 11  1         //此时100减111<0,不够减,所以商的第4高位为0
    


    除数继续右移
    。。。。。。
    如此循环,最后答案为101010100,最前面的1010就是前面四次计算的值。虽然很麻烦,但用电路来实现速度很快。

  • 相关阅读:
    UESTC_秋实大哥与时空漫游 2015 UESTC Training for Graph Theory<Problem C>
    UESTC_秋实大哥带我飞 2015 UESTC Training for Graph Theory<Problem B>
    UESTC_秋实大哥与连锁快餐店 2015 UESTC Training for Graph Theory<Problem A>
    UESTC_酱神寻宝 2015 UESTC Training for Dynamic Programming<Problem O>
    UESTC_导弹拦截 2015 UESTC Training for Dynamic Programming<Problem N>
    [error] MFC错误不能将参数1从"const char [3]"转换为"const wchar_t *"
    DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏的详细解释
    error MSB4044: 未给任务“CppClean”的必需参数“FoldersToClean”赋值
    关于vs2008的error C2440
    MFC m_hWnd 和 this指针
  • 原文地址:https://www.cnblogs.com/suprise/p/3086777.html
Copyright © 2020-2023  润新知