有没有big.BitCount?

时间:2013-09-30 23:57:19

标签: go bitcount

big.Int是否有一个已编写的BitCount方法?数学/大似乎没有。

显然,如果没有,我会自己写一个 - 是否有人已经写过?

我想要数字中的设置位数。与Java BigInteger.bitCount()一样。

4 个答案:

答案 0 :(得分:6)

如前所述,要快速有效地原始访问big.Int的基础位,您要使用big.Bits。 此外,比8位查找表或简单循环更快的是使用众所周知的64位计数位方法(aka Hamming weight)。 更快,您可以使用popcount的汇编实现,它使用本地CPU instruction¹。

如果不使用汇编,或者在知道设置的位数很少的特殊情况下,这可能是速度更快/最快的Go实现之一(使用uint32可以在32位机器上更快地实现相应地调整popcount功能):

func BitCount(n *big.Int) int {
    count := 0
    for _, v := range n.Bits() {
        count += popcount(uint64(v))
    }
    return count
}

// Straight and simple C to Go translation from https://en.wikipedia.org/wiki/Hamming_weight
func popcount(x uint64) int {
    const (
        m1  = 0x5555555555555555 //binary: 0101...
        m2  = 0x3333333333333333 //binary: 00110011..
        m4  = 0x0f0f0f0f0f0f0f0f //binary:  4 zeros,  4 ones ...
        h01 = 0x0101010101010101 //the sum of 256 to the power of 0,1,2,3...
    )
    x -= (x >> 1) & m1             //put count of each 2 bits into those 2 bits
    x = (x & m2) + ((x >> 2) & m2) //put count of each 4 bits into those 4 bits
    x = (x + (x >> 4)) & m4        //put count of each 8 bits into those 8 bits
    return int((x * h01) >> 56)    //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}

这里介绍的这个和其他实现的基准和比较可以在GitHub gist完整地获得。

¹如one added in Go1.9;更新后的要点显示它比我之前的最佳效果快3倍。

答案 1 :(得分:3)

我自己把一个放在一起 - 请注意,会考虑数字的符号。这将返回big.Int后面的原始字节的位数。

// How many bits?
func BitCount(n big.Int) int {
    var count int = 0
    for _, b := range n.Bytes() {
        count += int(bitCounts[b])
    }
    return count
}

// The bit counts for each byte value (0 - 255).
var bitCounts = []int8{
    // Generated by Java BitCount of all values from 0 to 255
    0, 1, 1, 2, 1, 2, 2, 3, 
    1, 2, 2, 3, 2, 3, 3, 4, 
    1, 2, 2, 3, 2, 3, 3, 4, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    1, 2, 2, 3, 2, 3, 3, 4, 
    2, 3, 3, 4, 3, 4, 4, 5,  
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    1, 2, 2, 3, 2, 3, 3, 4, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    4, 5, 5, 6, 5, 6, 6, 7, 
    1, 2, 2, 3, 2, 3, 3, 4, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    4, 5, 5, 6, 5, 6, 6, 7, 
    2, 3, 3, 4, 3, 4, 4, 5, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    4, 5, 5, 6, 5, 6, 6, 7, 
    3, 4, 4, 5, 4, 5, 5, 6, 
    4, 5, 5, 6, 5, 6, 6, 7, 
    4, 5, 5, 6, 5, 6, 6, 7, 
    5, 6, 6, 7, 6, 7, 7, 8,
}

答案 2 :(得分:2)

仅供参考,以下解决方案比此处提供的原始解决方案更简单,更快捷:

Dim Count As Integer
Dim RawOrders() As String
Dim Orders() As Variant
Dim y As Integer
Dim h As Integer

FileNum = FreeFile()
Open FileName For Input As #FileNum
    RawOrders = Split(Input$(LOF(FileNum), #FileNum), vbNewLine)
Close #FileNum

Count = UBound(RawOrders, 1)

ReDim Orders(Count - 1)

h = 1
y = 0

Do While Not RawOrders(h) = ""

    Orders(y) = Split(RawOrders(h), ",")

    y = y + 1
    h = h + 1
Loop

它在我的机器上超过原始解决方案5倍:

func BitCountFast(z *big.Int) int {
    var count int
    for _, x := range z.Bits() {
            for x != 0 {
                    x &= x-1
                    count++
            }
    }
    return count
}

答案 3 :(得分:2)

你现在可以使用(从Go 1.9开始)math/bits库来实现一些处理与位相关的计算的有用函数。具体来说,您可以遍历big.Int.Bits的结果并调用bits.OnesCount函数。

以下是一个例子:

package main

import (
    "fmt"
    "math/big"
    "math/bits"
)

func BitCount(z *big.Int) int {
    var count int
    for _, x := range z.Bits() {
        count += bits.OnesCount(uint(x))
    }
    return count
}

func PrintBinary(z *big.Int) {
    for _, x := range z.Bits() {
        fmt.Printf("%064b\n", x)
    }
}

func main() {
    a := big.NewInt(1 << 60 - 1)
    b := big.NewInt(1 << 61 - 1)
    c := big.NewInt(0)
    c = c.Mul(a, b)

    fmt.Println("Value in binary format:")
    PrintBinary(c)
    fmt.Println("BitCount:", BitCount(c))
}