位向量与布尔值列表的性能

Posted

技术标签:

【中文标题】位向量与布尔值列表的性能【英文标题】:Bit vector vs list of boolean values performance 【发布时间】:2016-04-11 17:08:54 【问题描述】:

我试图用 Python 重现我在书中找到的两个示例(最初用 Java 编写)。

这两个函数检查字符串是否包含重复字符。第一个函数使用整数 (checker) 作为位向量,而第二个函数仅使用布尔值列表。 我期待使用带有位的函数可以获得更好的性能,但实际上它的性能更差。

这是为什么呢?从Java“翻译”到Python时我写错了吗?

注意: 为简单起见,我们只使用小写字母(az >),尤其是位向量函数。

import sys
import timeit

def is_unique_chars_bit(my_str):
    checker = 0
    for char in my_str:
        val = ord(char) - ord('a')
        if ((checker & (1 << val)) > 0):
            return False
        checker |= (1 << val)
    return True

def is_unique_chars_list(my_str):
    if len(my_str) > 128:
        # Supposing we use ASCII, which only has 128 chars
        return False
    char_list = [False] * 128
    for char in my_str:
        val = ord(char)
        if char_list[val]:
            return False
        char_list[val] = True
    return True

if __name__ == '__main__':
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    t_bit = timeit.Timer("is_unique_chars_bit('"+ alphabet +"')", "from __main__ import is_unique_chars_bit")
    t_list = timeit.Timer("is_unique_chars_list('"+ alphabet +"')", "from __main__ import is_unique_chars_list")
    print(t_bit.repeat(3, 200000))
    print(t_list.repeat(3, 200000))

结果:

[1.732477278999795, 1.7263494359995093, 1.7404333820004467]
[0.6785205180003686, 0.6759967380003218, 0.675434408000001]

原来的Java函数如下:

boolean isUniqueCharsBoolArray(String str) 
    if (str.length() > 128) return false;

    boolean[] char_set = new boolean[128];
    for (int i = 0; i < str.length(); i++) 
        int val = str.charAt(i);
        if (char_set[val]) 
            return false;
        
        char_set[val] = true;
    
    return true;


boolean isUniqueCharsBits(String str) 
    for (int i = 0; i < str.length(); i++) 
        int val = str.charAt(i) -'a';
        if ((checker & (1 << val)) > 0) 
            return false;
        
        checker |= (1 << val);
    
    return true;

【问题讨论】:

【参考方案1】:

那是因为整数在 python 中是不可变的引用类。这意味着整数运算通常很慢。 (即使对于 python2 整数也是如此)查看以下行:

checker |= (1 << val)

如果我们扩展我们得到的赋值:

checker = checker | (1 << val)

这一行在内存中分配了两个新整数。一个用于1 &lt;&lt; val,一个用于按位或。

另一方面,分配数组元素不需要分配对象,这就是它更快的原因。

如果你正在寻找最快的方法来判断一个字符串是否有重复字符,这个函数比前两个 (取自"check duplicates in list"):

def is_unique_chars_set(my_str):
    return len(my_str) != len(set(my_str))

Timeit 显示了 3 倍的加速(最后一行是新的):

>python3 test.py
[2.398782472571393, 2.3595238689519626, 2.37358552995787]
[1.0055455609592512, 1.007462347465074, 1.012826469701654]
[0.32564058749026437, 0.3303359144351621, 0.31772896318809885]

注意:如果您使用其他 python 运行时,例如 IronPython,结果可能会有很大差异

【讨论】:

感谢您提供了一个额外的方法:我没有考虑它,因为我只是翻译了上面的两个Java方法,但它可以作为参考。你说“改变数组根本不分配对象”;你能提供一些参考吗?您是在谈论一般的数组,还是只是这个特定的列表(我们只更改 True/False 值)?此外:这是否应该在 Java 中产生相反的性能? @KurtBourbaki (与此同时,我试图启动逐行分析器,但没有成功。)我在谈论一般的列表,我的意思是项目分配。 ([].append 会导致内存分配,但被列表对象本身隐藏) 所以这更快,因为我们分配 TrueFalse 值而不是分配新的整数,对吧?你对Java有一些了解吗?例如:位向量会比布尔数组更有效吗? @KurtBourbaki 我认为 java BitSetboolean[] 之间的访问时间没有很大差异(参见 ***.com/questions/605226/…,它说 boolean[] 更快)。前者的主要优点是使用较少的内存,并且预定义了一些简洁的按位运算。

以上是关于位向量与布尔值列表的性能的主要内容,如果未能解决你的问题,请参考以下文章

布尔&list与条件循环语句与trutle

第一章 数据储存 1.1 位与位存储

JS:将布尔列表转换为紧凑字符串?

导出的 C 函数与结构与 .Net 编组中的布尔大小

Python学习之旅---数据类型(数字字符窜列表元组字典布尔值)

如何在 GCC 下强制设置“布尔”的大小