`
chenchao051
  • 浏览: 138006 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

为啥要用位运算代替取模呢

阅读更多

 

  在hash中查找key的时候,经常会发现用&取代%,先看两段代码吧,

 

  JDK6中的HashMap中的indexFor方法:

    /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

 

Redis2.4中的代码段:

    n.size = realsize;
    n.sizemask = realsize-1;
    //此处略去xxx行
   while(de) {
            unsigned int h;

            nextde = de->next;
            /* Get the index in the new hash table */
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = nextde;
        }

 

大家可以看到a%b取模的形式都被替换成了a&(b-1) ,当hashtable的长度是2的幂的情况下(疏忽,一开始没写),两者是等价的,那为什么要用后者呢?

另一方面,为什么hashtable的长度最好要是2的n次方呢,这个不在本次讨论范围之列,原因简单说一下就是1、分布更均匀 2、碰撞几率更小  详情自己思考,JDK中的HashMap就会在初始化时,保证这一点:

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

 

redis中也有类似的保证:

 

/* Our hash table capability is a power of two */
static unsigned long _dictNextPower(unsigned long size)
{
    unsigned long i = DICT_HT_INITIAL_SIZE;

    if (size >= LONG_MAX) return LONG_MAX;
    while(1) {
        if (i >= size)
            return i;
        i *= 2;
    }
}

 

 

言归正传,大家都知道位运算的效率最高,这也是&取代%的原因,来看个程序:

int main(int argc, char* argv[])
{
    int a = 0x111;
    int b = 0x222;
    int c = 0;
    int d = 0;

    c = a & (b-1);
    d = a % b;

    return 0;
}

 

 

看反汇编的结果:

13:       c = a & (b-1);
00401044   mov         eax,dword ptr [ebp-8]
00401047   sub         eax,1
0040104A   mov         ecx,dword ptr [ebp-4]
0040104D   and         ecx,eax
0040104F   mov         dword ptr [ebp-0Ch],ecx
14:       d = a % b;
00401052   mov         eax,dword ptr [ebp-4]
00401055   cdq
00401056   idiv        eax,dword ptr [ebp-8]
00401059   mov         dword ptr [ebp-10h],edx

 

可以看到,&操作用了:3mov+1and+1sub  %操作用了:2mov+1cdp+1idiv

 

我们可以查阅Coding_ASM_-_Intel_Instruction_Set_Codes_and_Cycles资料,发现前者只需5个CPU周期,而后者至少需要26个CPU周期(注意,是最少!!!) 效率显而易见。所以以后自己在写的时候,也可以使用前者的写法。

 

 

10
4
分享到:
评论
13 楼 masuweng 2012-12-27  
masuweng 写道
写的不错。

12 楼 freezingsky 2012-11-15  
在各种四则运算中,位运算是最快的。在不少C方面的笔试里,会出现位运算的题目。考察的内容包括左移,右移,取反等。
11 楼 elan1986 2012-11-15  
不错! 
10 楼 暴走娘 2012-11-15  
等价的,那可以直接代替了!
9 楼 masuweng 2012-11-15  
写的不错。
8 楼 madiot 2012-11-15  
chenchao051 写道
madiot 写道
b是2的n次方才等价,这就是为什么hashtable的长度是2的n次方,这还不在本文讨论范围呢,汗啊~~~

ht2次方并不完全是因为这个原因,更多是因为hash的分布均匀与减少hash碰撞。

说的是
7 楼 chenchao051 2012-11-14  
madiot 写道
b是2的n次方才等价,这就是为什么hashtable的长度是2的n次方,这还不在本文讨论范围呢,汗啊~~~

ht2次方并不完全是因为这个原因,更多是因为hash的分布均匀与减少hash碰撞。
6 楼 chenchao051 2012-11-14  
groovyzhou 写道
不等价,除非length是2的幂

感谢各位,我疏忽了,没写清楚,文章已改。
5 楼 groovyzhou 2012-11-14  
不等价,除非length是2的幂
4 楼 madiot 2012-11-14  
b是2的n次方才等价,这就是为什么hashtable的长度是2的n次方,这还不在本文讨论范围呢,汗啊~~~
3 楼 yanghdx 2012-11-14  
难道是b%2=0时 等价?
2 楼 空谷悠悠 2012-11-14  
果然,iteye推到首页就看到了这篇blog,明显不等价
1 楼 iminto 2012-11-14  
这两个不等价吧,我测试了结果不一样

相关推荐

    位运算.pdf 精心收集的资料,重新编辑

    - 对2的倍数取模:`a & (n-1)`可以代替`a % n`,对于n为2的幂次方特别有效。 - 越界判断:如`a & ~((1 )-1)`可以判断a是否在0到65535之间。 2. 位运算符介绍: - `&`(与运算):对应位都是1时结果为1,否则为0...

    [纯C语言 + Win32 API]一步一步写个围棋程序之十八:代码的改良

    在上面的例子中,我们利用位左移()来替代乘法,并且用按位与(&)和常量`wrap`来代替取模运算,这样就减少了CPU的运算负担。 在实际编程过程中,我们还需要考虑代码的可读性和维护性。虽然位运算可以提高性能,但...

    21067010433-王珍-输出随机三位数-解题报告1

    然后,通过取模运算 `%` 和整除 `/` 来获取百位、十位和个位数字。最后,使用`System.out.println`打印出结果。 代码解释如下: - `Random sc=new Random();` 创建一个随机数生成器对象`sc`。 - `int number=sc....

    Python语言整数运算实现机制分析与性能评估.zip

    - 使用内置函数(如`pow()`)代替自定义的指数运算,因为它们通常经过优化。 - 在需要高性能计算时,可以考虑使用NumPy等科学计算库,它们使用C扩展,性能更优。 综上所述,Python整数运算的实现机制是其动态特性...

    HashMap源码中的位运算符&详解

    在HashMap源码中,我们可以看到许多使用位运算符&代替取模运算符%的现象。那么,这是为什么呢?首先,我们需要了解取模运算符%的底层原理。取模运算符%实际上是除法器,a % b 等同于 a - a / b * b。这种运算方式的...

    Java解惑(中文)

    此外,在性能敏感的场景下,可以利用位运算代替取模运算,提高效率: ```java public static boolean isOdd(int i) { return (i & 1) != 0; } ``` #### 谜题2:二进制浮点数的精度问题 另一个常见陷阱涉及二进制...

    斐波那契数列.rar

    斐波那契数列的几种时间复杂度优化 以下代码因不同算法而时间复杂度不同个人归类为不同版本,总结如下。 1.尽量不要用递归,纵使好看,但...4.用位运算来代替乘法、除法以及取模。 5.有数学公式用数学公式@.@....

    C语言如何将int转byte类型

    在C语言中,数据类型是...在进行类型转换时,要确保 `int` 值在适当的范围内,或者使用适当的位运算和取模运算来处理溢出和负数。在实际编程中,应该根据具体需求选择合适的方法,并进行充分的测试以确保转换的正确性。

    Url消重算法(BloomFilter)

    本程序主要是BloomFilter算法的简化实现 因为C#非安全代码无法直接分配内存块,使用了int型数组代替,暂时为了简单没有使用位运算,比位运算消耗内存多16倍。 算法原理: 其首先申请一块大内存,并把内存中...

    MATLAB中mod函数转化为C语言,有项目算法使用matlab中mod函数进行运算,这里需要将转化为C语言,从而模拟算法运行

    在MATLAB中,`mod`函数是一个非常常用的数学运算符,用于执行取模运算,即求两个数值相除后的余数。在C语言中,我们可以使用 `%` 运算符来实现相同的功能。以下是关于MATLAB `mod`函数转换为C语言的详细解释: ## ...

    编写单片机C语言代码的技巧和经验

    如果需要计算余数,可以使用与(&)操作符代替取模(%)操作符。 5. 优化循环和延时函数:循环是程序中常见的结构,应尽量减少循环的次数和复杂度。例如,在for循环中,将操作移至循环外部执行,减少每次循环中的...

    C语言代码优化11种实用方法.pdf

    - **位操作优化**:位操作(如按位与`&`)通常比算术运算(如取模`%`)更快。例如,用`a=a&7`替换`a=a%8`,对于要求2的幂次方的余数尤其有效。 - **避免浮点运算**:浮点数的运算通常比整数运算慢,因此应尽量避免...

    哈希表相关概念、hash函数、hash冲突解决方案、代码示例

    4. **位与法**:如果哈希表长度是2的幂,可以利用位运算代替取模,提高计算效率。例如,`Hash(K) = K & (length - 1)`。 5. **折叠法**:将键分割成若干等长或不等长的部分,将各部分相加得到哈希值。适合处理位数...

    c 高级技术 C语言代码优化方案

    在处理取模运算时,可以考虑使用位运算来进行优化,特别是当除数是2的幂次方时,使用位与操作代替取模运算会更加高效。 ### 总结 通过上述介绍可以看出,C语言代码优化涉及多个方面,包括但不限于选择合适的数据...

    dsp中c代码优化

    - **移位运算代替乘除法**:对于乘以或除以2的幂次,使用移位操作(`和`&gt;&gt;`)可以显著提高速度,如`a=a代替`a=a*4`,`b=b&gt;&gt;2`代替`b=b/4`。 4. **其他优化技巧**: - **避免冗余计算**:重复计算相同的表达式会...

    ARM的除法运算优化策略

    例如,若需要频繁地进行n/d的运算,可以先计算s=(2^k-1)/d,然后通过2^k位乘法来代替k位无符号整数除法。这种方法特别适用于同一个除数的重复运算,如3D坐标变换中的归一化操作。此外,避免浮点除法,使用整数运算,...

    JAVA解惑(JAVA谜题) 中文版(PDF)

    此外,在性能敏感的应用场景中,可以使用位操作符`&`代替取模运算: ```java public static boolean isOdd(int i) { return (i & 1) != 0; } ``` 这样做的好处是提高了运算效率。 #### 谜题2:找零问题 第二个谜题...

    javascript文档

    &= 运算符 对变量和表达式执行按位“与”运算,结果赋给变量。 && 运算符 对两个表达式执行逻辑连接运算。 * 运算符 将两个表达式的值相乘。 *= 运算符 将变量与表达式的值相乘,结果赋给变量。 + 运算符 将两个...

    节 数 据 运 算c语言PPT学习教案.pptx

    这些操作通常用于低级别编程,如设备驱动程序和内存管理,甚至可以用位移来代替乘除运算,提高效率。 7. **数学函数**:通过包含`&lt;math.h&gt;`头文件,C语言提供了丰富的数学函数,如正弦(sin), 余弦(cos), 正切(tan)...

    实验1 原码除法器设计1

    在设计过程中,禁止直接使用Verilog中的除法和取模运算符,而是要利用恢复余数算法或加减交替算法来实现。 **原码除法原理**: 原码除法中,符号位是独立处理的。假设被除数为[x]原,除数为[y]原,它们的绝对值分别...

Global site tag (gtag.js) - Google Analytics