【问题标题】:Using bitwise storage of bools in a DP problem在 DP 问题中使用布尔的按位存储
【发布时间】:2019-11-22 18:21:45
【问题描述】:

我在 this leetcode 工作,我想知道如何使用按位操作。原因是,当我使用 lru_cache 时,我得到 isUsed is not hashable 的错误。代替 isUsed 的 bool 数组,使用 int 和按位运算而不是 bool 的最佳做法是什么

def canIWin(self, n: int, target: int) -> bool:
    @lru_cache(None)
    def isWin(isUsed, target):
        # print(isUsed,target)
        if target<=0:
            return False
        for i in reversed(range(1,n+1)):
            if not isUsed[i-1]:
                if i>=target:
                    return True
                isUsed[i-1] = True
                if not isWin(isUsed[::], target-i):
                    return True
                isUsed[i-1] = False

        return False

    if target <2:
        return True
    if n*(n+1)/2<target:
        return False
    return isWin([False]*n,target)

【问题讨论】:

  • 如果您的代码使用列表工作,唯一的问题是列表不可散列,请尝试使用元组。如果结果证明效率不够高,那么只有这样才值得重写算法以使用整数作为位集。
  • 我猜 tuple 的问题在于它是不可变的。我想我可以来回转换它
  • 是的,来回转换将是最简单的解决方法。在isWin函数的开头写isUsed = list(isUsed),把if not isWin(isUsed[::], ...):改成if not isWin(tuple(isUsed), ...):

标签: python bit-manipulation


【解决方案1】:

正如 kaya3 在他们的评论中提到的,您可以只使用元组而不是列表,而不是使用整数位集,因为它们是不可变的和可散列的。在元组中设置一个位有点复杂,所以将元组转换为列表可能更容易一旦你在函数内部


如果你真的想使用 bitset,你必须用 2 的幂进行一些 ANDing、ORing 和 XORing:

在长度为 n 的位集中的索引 i 处设置位:

bitset = bitset | 2**(n-1-i)

稍微重置一下:

bitset = bitset & (2**n-1) ^ 2**(n-1-i)

【讨论】:

    【解决方案2】:

    您可以使用元组或使用位集。使用元组最简单的方法是将其转换为数组并将其转换回来,但是它更快地将值数组切片并传递,而不是传递布尔数组。

    以下是三种方法:

    1. 元组切片无布尔数组(388 毫秒)这是最快的,因为您可以通过检查最后一项来提前退出
    2. 使用位集(532 毫秒)
    3. 从列表转换为元组(624 毫秒)

    元组切片:

    def canIWin(self, n: int, target: int) -> bool:
        @lru_cache(None)
        def isWin(nums, target):
            if target <= 0:
                return False
            n = len(nums)
            if nums[-1] >= target:
                return True
            for i in range(n):
                if not isWin(nums[:i] + nums[i + 1:], target - nums[i]):
                    return True
            return False
        if target < 2:
            return True
        if (n + 1) * n / 2 < target:
            return False
        return isWin(tuple(range(1, n + 1)), target)
    

    位集:

    def canIWin(self, n: int, target: int) -> bool:
        isNthBitSet = lambda x, n: (x & (1 << n) != 0)
        setNthBit = lambda x, n: x | (1 << n)
        @lru_cache(None)
        def isWin(isUsed, target):
            if target <= 0:
                return False
            for i in reversed(range(n)):
                if not isNthBitSet(isUsed, i):
                    if not isWin(setNthBit(isUsed, i), target - i - 1):
                    return True
        return False
    if target < 2:
        return True
    if n * (n + 1) / 2 < target:
        return False
    return isWin(0, target)
    

    元组列表:

    def canIWin(self, n: int, target: int) -> bool:
        @lru_cache(None)
        def isWin(isUsed, target):
            isUsed = list(isUsed)
            if target <= 0:
                return False
            for i in reversed(range(1, n + 1)):
                if not isUsed[i - 1]:
                    if i >= target:
                        return True
                    isUsed[i - 1] = True
                    if not isWin(tuple(isUsed[::]), target - i):
                        return True
                    isUsed[i - 1] = False
            return False
        if target < 2:
            return True
        if n * (n + 1) / 2 < target:
            return False
        return isWin(tuple([False] * n), target)
    

    【讨论】:

    • 那个 bitset 真的很慢。有些模块的速度要快得多,因为它们无需对 Python 整数进行按位运算即可访问位数组。
    猜你喜欢
    • 1970-01-01
    • 2011-08-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 2012-03-10
    相关资源
    最近更新 更多