Golang 程序实现位集

go programmingserver side programmingprogramming

BitSet 是一种数据结构,表示一组固定大小的二进制值,其中每个值可以是 0 或 1。它通常用于高效存储和操作大量布尔值。在本文中,我们将使用两种不同的方法在 Go 中实现位集,第一种方法涉及使用布尔值切片,第二种方法涉及使用无符号整数的位操作。这里的实现意味着我们将执行各种操作,例如设置、清除和测试位集数据结构中的各个位。

解释

BitSet 是一种数据结构,用于高效存储和操作一组二进制值,其中每个值可以是 0 或 1。它在需要跟踪大量布尔值(例如表示集合中元素的成员资格或存在)的场景中特别有用。

Bitset:[0, 1, 0, 1, 0, 0, 1, 0]
Index:0 1 2 3 4 5 6 7

语法

type BitSet struct{ data []uint64; size int }

语法 BitSet 结构表示使用 uint64 切片实现的 BitSet。 data 字段是一个 uint64 切片,用于存储 BitSet 的实际位。size 字段表示 BitSet 中的总位数。此方法在处理大量位时内存效率高,并且对 uint64 元素执行按位操作以有效地操作各个位。

算法

  • 使用 uint64 的空数据切片创建一个 BitSet 结构,并将大小设置为 BitSet 中所需的位数。

  • 要在特定位置 pos 设置位,请使用 pos / 64 计算数据切片中 uint64 元素的索引,并使用 pos % 64 计算该位在该 uint64 元素中的位置。使用按位或 (|) 在 uint64 元素中设置计算位置的位。

  • 要清除特定位置 pos 的位,请计算 uint64 元素的索引和该位在该元素中的位置,类似于设置操作。使用按位与 (&) 与位掩码的否定来清除计算位置处的位。

  • 要切换特定位置 pos 处的位,请计算 uint64 元素的索引和该元素内的位的位置,类似于设置操作。使用位掩码按位异或 (^) 切换计算位置的位。

  • 并集、交集和差集:要对两个 BitSet 执行并集、交集和差集等集合运算,请在两个 BitSet 的数据切片元素之间应用相应的按位运算(OR、AND 和 XOR)。

  • 要对 BitSet 执行移位和旋转运算,请对数据切片元素使用按位移位和旋转运算将位向左或向右移动。

示例 1

在此示例中,我们将使用 uint64 切片在 go 中实现 BitSet。切片中的每个 uint64 元素可以表示 64 位,使我们能够高效地处理大量位。NewBitSet 函数使用指定的大小初始化 BitSet。 Set 函数在给定的索引处设置特定的位,Clear 函数清除给定索引处的位,Test 函数检查给定索引处是否设置了位。

package main

import "fmt"

type BitSet struct {
	bitArray []uint64
	size     int
}

func NewBitSet(size int) *BitSet {
	sliceSize := (size + 63) / 64
	return &BitSet{
		bitArray: make([]uint64, sliceSize),
		size:     size,
	}
}

func (bs *BitSet) Set(bit int) {
	if bit < 0 || bit >= bs.size {
		return
	}

	index := bit / 64
	offset := bit % 64
	bs.bitArray[index] |= 1 << offset
}

func (bs *BitSet) Clear(bit int) {
	if bit < 0 || bit >= bs.size {
		return
	}

	index := bit / 64
	offset := bit % 64
	bs.bitArray[index] &= ^(1 << offset)
}

func (bs *BitSet) Test(bit int) bool {
	if bit < 0 || bit >= bs.size {
		return false
	}

	index := bit / 64
	offset := bit % 64
	return (bs.bitArray[index] & (1 << offset)) != 0
}

func main() {
    bitSet := NewBitSet(128)
    
    bitSet.Set(1)
    bitSet.Set(3)
    bitSet.Set(5)
    bitSet.Set(120)
    
    fmt.Println("位置 1 的位被设置:", bitSet.Test(1))
    fmt.Println("位置 10 的位被设置:", bitSet.Test(10))
    fmt.Println("位置 3 的位被设置:", bitSet.Test(3))

}

输出

位置 1 的位被设置:true
位置 10 的位被设置:false
位置 3 的位被设置:true

示例 2

在此示例中,我们将使用固定大小的整数数组在 Go 中实现 BitSet。这里每个整数代表固定数量的位。例如,使用 32 位整数,我们可以表示 32 位。我们将演示如何设置、清除和测试 BitSet 中的各个位。我们将使用 32 位整数数组来表示 BitSet,其中每个元素可以容纳 32 位。

package main

import (
	"fmt"
)

type BitSet struct {
	bitArray []uint32
	size     int
}

func NewBitSet(size int) *BitSet {
	numElements := (size + 31) / 32
	return &BitSet{
		bitArray: make([]uint32, numElements),
		size:     size,
	}
}

func (bs *BitSet) Set(bit int) {
	if bit < 0 || bit >= bs.size {
		return
	}

	element := bit / 32
	bitWithinElement := bit % 32
	bs.bitArray[element] |= (1 << uint32(bitWithinElement))
}

func (bs *BitSet) Clear(bit int) {
	if bit < 0 || bit >= bs.size {
		return
	}

	element := bit / 32
	bitWithinElement := bit % 32
	bs.bitArray[element] &= ^(1 << uint32(bitWithinElement))
}

func (bs *BitSet) Test(bit int) bool {
	if bit < 0 || bit >= bs.size {
		return false
	}

	element := bit / 32
	bitWithinElement := bit % 32
	return bs.bitArray[element]&(1<<uint32(bitWithinElement)) != 0
}

func main() {
    bitSet := NewBitSet(128)
    
    bitSet.Set(1)
    bitSet.Set(3)
    bitSet.Set(5)
    bitSet.Set(120)
    
    fmt.Println("位置 1 的位已设置:", bitSet.Test(1))
    fmt.Println("位置 10 的位已设置:", bitSet.Test(10))
    fmt.Println("位置 3 的位已设置:", bitSet.Test(3))

}

输出

位置 1 的位已设置:true
位置 10 的位已设置:false
位置 3 的位已设置:true

实际实现

网络数据包过滤

BitSet 在网络中用于数据包过滤,路由器和防火墙将传入的数据包与规则进行匹配。每条规则都表示为一个 BitSet,其中的位表示条件,如源 IP 范围、协议或数据标志。高效的按位运算有助于快速决定是否允许数据包。

稀疏数据索引

在数据库和搜索引擎中,BitSet 索引用于稀疏数据。它将属性压缩为位,从而节省内存。例如,在搜索引擎中,BitSet 识别包含特定术语的文档。设置位表示术语存在,使索引和查询更快。

结论

BitSet 是一种数据结构,用于高效存储和操作一组二进制值,其中每个值可以是 0 或 1。在本文中,我们研究了如何在 Go 中实现 BitSet:一个使用 uint64 切片,另一个使用固定大小的整数数组。第一个例子在处理大量位数时内存效率较高,而第二个例子在处理较少位数时内存效率更高。


相关文章