简体   繁体   English

有big.BitCount吗?

[英]Is there a big.BitCount?

Is there an already-written BitCount method for big.Int? BitCount是否已经编写了BitCount方法? There doesn't seem to be one in math/big. 数学/大数学似乎没有一个。

Obviously I will write one myself if not - does anyone have one already written? 显然,我会自己写一个-有人写过吗?

I want the number of set bits in the number. 我想要数量中的设定位数。 Like Java BigInteger.bitCount() . Java BigInteger.bitCount()一样

As already mentioned, for quick efficient raw access to the underlying bits of a big.Int you want to use big.Bits . 如前所述,要快速有效地原始访问big.Int的基础位,您需要使用big.Bits Also, quicker than either an 8 bit lookup table or a simple loop is to use one of well know 64 bit methods of counting bits (aka Hamming weight ). 而且,比使用8位查找表或简单循环更快的方法是,使用众所周知的64位计数方法之一(又称汉明权重 )。 Even faster, you could use an assembly implementation of popcount that uses a native CPU instruction ¹. 甚至更快,您可以使用使用本机CPU指令 ¹的popcount汇编实现。

Without using assembly, or catering to special cases where it's known there are few bits set, this is likely one of the faster/fastest Go implementations (it could be made faster on 32 bit machines by using uint32 and adjusting the popcount function accordingly): 如果不使用汇编程序,或者不满足已知的特殊情况(设置的位数很少),则这可能是最快/最快的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) + ...
}

Benchmarks and comparisons of this and other implementations presented here is available in full on GitHub gist . 此处提供的此实现和其他实现的基准测试和比较可在GitHub gist上完整获得。

¹ Such as the one added in Go1.9 ; ¹如Go1.9中添加的那一项 the updated gist shows it is ~3× faster than the previous best I had. 更新的要点显示它比我以前的最佳速度快约3倍。

I put one together myself - note that this does not take into account the sign of the number. 我自己整理了一个-请注意,这并未考虑数字的符号。 This returns the bit count of the the raw bytes behind the big.Int . 这将返回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,
}

FYI, the following solution is simpler and faster than the original solution provided here: 仅供参考,以下解决方案比此处提供的原始解决方案更简单,更快捷:

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

It outperforms the original solution by 5x on my machine: 在我的机器上,它的性能比原始解决方案高出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

You can use now (as of Go 1.9) the math/bits library that implements some useful functions that deal with bit-related computations. 现在(从1.9版开始),您可以使用math/bits库,该库实现一些处理与位相关的计算的有用函数。 Concretely, you can iterate through the result of big.Int.Bits and call the bits.OnesCount function. 具体来说,您可以遍历big.Int.Bits的结果并调用bits.OnesCount函数。

Here is an example: 这是一个例子:

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