【问题标题】:O(1) set bit countingO(1) 设置位计数
【发布时间】:2020-05-26 23:40:18
【问题描述】:

我正在查看这个设置位计数页面:https://www.geeksforgeeks.org/count-set-bits-in-an-integer/

最后一个算法用位映射数字说:它只是维护一个数字到位的映射(或数组)以用于半字节。一个半字节包含 4 个位。所以我们需要一个最多 15 个的数组。

int num_to_bits[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};

现在我们只需要递归地获取给定的 long/int/word 等的半字节。

num_to_bits =[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];  

# Recursively get nibble of a given number  
# and map them in the array 
def countSetBitsRec(num): 
    nibble = 0; 
    if(0 == num): 
        return num_to_bits[0]; 

    # Find last nibble 
    nibble = num & 0xf; 

    # Use pre-stored values to find count 
    # in last nibble plus recursively add 
    # remaining nibbles. 

    return num_to_bits[nibble] + countSetBitsRec(num >> 4);  

num = 31
from timeit import default_timer as timer
t1 = timer()
print(countSetBitsRec(num))
t2 = timer()
print(t2-t1)
num = 421342356246244235625423523626342453143523624526434636546745745634523546346346346346344506546456909546540964596956306030963068359683578753068340634960340683463906835096835068309683486036830563596
t1 = timer()
print(countSetBitsRec(num))
t2 = timer()
print(t2-t1)

t1 = timer()
print(bin(num).count('1'))
t2 = timer()
print(t2-t1)
5
0.00013369599992074654
335
0.00015420899990203907
335
0.00011028399990209437

在时间复杂度部分,它表示时间和内存都是 O(1)。即使两个整数的时间都很接近,我也无法理解这是 O(1) 的情况,因为它正在进行递归调用?

【问题讨论】:

  • 很抱歉,geeksforgeeks 是出了名的错误或不精确。这不会是第一次。在这种情况下,他们首先声称整数的长度是可变的(由于机器架构,这在实践中是不正确的)然后他们假设它的长度是一个常数来逃避 O(1)。在这个特定级别上,时间复杂度绝对无关紧要。在这个级别,您只关心有效性能,而不是理论复杂性。
  • 是的,我现在明白了。我还认为 64 位整数只有 O(1),但他们没有提到。这真的很混乱。谢谢。

标签: python-3.x bit-manipulation


【解决方案1】:

内存复杂度显示为O(1),基于它使用固定数组的事实(但有很多递归调用,并且内存不会是 O(1))。

时间复杂度不是O(1),每次nibble = num & 0xf;这给出一个小于等于15的整数(0xf15&运算确保结果不大于@987654327 @)。然后它递归地将这些索引用于半字节。

我们可以计算两个数字的步数进行比较;如果运行时为O(1),则步数应相同。

num_to_bits =[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];  

# Recursively get nibble of a given number  
# and map them in the array 

num_steps = 0
def countSetBitsRec(num): 
    global num_steps
    num_steps += 1
    nibble = 0; 
    if(0 == num): 
        return num_to_bits[0]; 

    # Find last nibble 
    nibble = num & 0xf; 

    # Use pre-stored values to find count 
    # in last nibble plus recursively add 
    # remaining nibbles. 

    return num_to_bits[nibble] + countSetBitsRec(num >> 4);  

num = 31
from timeit import default_timer as timer
t1 = timer()
print(countSetBitsRec(num))
print(f'num steps: {num_steps}')
t2 = timer()
print(t2-t1)
num = 421342356246244235625423523626342453143523624526434636546745745634523546346346346346344506546456909546540964596956306030963068359683578753068340634960340683463906835096835068309683486036830563596
global num_steps
num_steps = 0
t1 = timer()
print(countSetBitsRec(num))
print(f'num steps: {num_steps}')
t2 = timer()
print(t2-t1)

5
num steps: 3
0.00024106499995468766
335
num steps: 163
0.0012161659999492258

如您所见,步数直接取决于整数大小本身。对于大整数,数字与长度成正比,而不是O(1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 2011-02-25
    • 2012-02-23
    • 1970-01-01
    • 1970-01-01
    • 2019-09-29
    相关资源
    最近更新 更多