繁体   English   中英

有big.BitCount吗?

[英]Is there a big.BitCount?

BitCount是否已经编写了BitCount方法? 数学/大数学似乎没有一个。

显然,我会自己写一个-有人写过吗?

我想要数量中的设定位数。 Java BigInteger.bitCount()一样

如前所述,要快速有效地原始访问big.Int的基础位,您需要使用big.Bits 而且,比使用8位查找表或简单循环更快的方法是,使用众所周知的64位计数方法之一(又称汉明权重 )。 甚至更快,您可以使用使用本机CPU指令 ¹的popcount汇编实现。

如果不使用汇编程序,或者不满足已知的特殊情况(设置的位数很少),则这可能是最快/最快的Go实现之一(可以通过使用uint32并相应地调整popcount函数在32位计算机上更快地实现):

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上完整获得。

¹如Go1.9中添加的那一项 更新的要点显示它比我以前的最佳速度快约3倍。

我自己整理了一个-请注意,这并未考虑数字的符号。 这将返回big.Int后面的原始字节的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,
}

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

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

在我的机器上,它的性能比原始解决方案高出5倍:

BenchmarkBitCountFast-4 100000000           19.5 ns/op         0 B/op          0 allocs/op
BenchmarkBitCountOrig-4 20000000            96.1 ns/op        16 B/op          1 allocs/op

现在(从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))
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM