有一个 big.BitCount 吗?

huangapple go评论89阅读模式
英文:

Is there a big.BitCount?

问题

math/big包中,没有现成的BitCount方法可用于big.Int。如果没有现成的方法,你可以自己编写一个。

以下是一个计算big.Int中设置位数的示例代码:

import (
	"math/big"
)

func BitCount(n *big.Int) int {
	count := 0
	zero := big.NewInt(0)
	two := big.NewInt(2)

	for n.Cmp(zero) > 0 {
		if new(big.Int).And(n, big.NewInt(1)).Cmp(big.NewInt(1)) == 0 {
			count++
		}
		n.Rsh(n, 1)
	}

	return count
}

你可以使用上述代码来计算big.Int中设置位的数量。这个方法使用了位运算和math/big包中的函数来实现。

英文:

Is there an already-written BitCount method for big.Int? 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().

答案1

得分: 6

如前所述,要快速高效地访问big.Int的底层位,您可以使用big.Bits
此外,比起8位查找表或简单循环,使用已知的64位比特计数方法(也称为汉明重量)更快。
更快的方法是使用使用本机CPU指令¹的popcount的汇编实现。

以下是一个不使用汇编的、不针对已知只有少量位设置的特殊情况的情况下,可能是Go中较快/最快的实现之一(在32位机器上可以通过使用uint32并相应调整popcount函数来使其更快):

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

// 从https://en.wikipedia.org/wiki/Hamming_weight直接简单地将C翻译为Go
func popcount(x uint64) int {
    const (
        m1  = 0x5555555555555555 //二进制: 0101...
        m2  = 0x3333333333333333 //二进制: 00110011..
        m4  = 0x0f0f0f0f0f0f0f0f //二进制: 4个零,4个一...
        h01 = 0x0101010101010101 //256的0、1、2、3次幂的和...
    )
    x -= (x >> 1) & m1             //将每2位的计数放入这2位中
    x = (x & m2) + ((x >> 2) & m2) //将每4位的计数放入这4位中
    x = (x + (x >> 4)) & m4        //将每8位的计数放入这8位中
    return int((x * h01) >> 56)    //返回x的左8位 + (x<<8) + (x<<16) + (x<<24) + ...的结果
}

这里提供了对此实现和其他实现的基准测试和比较的完整GitHub gist

¹ 例如,在Go1.9中添加的一个方法;更新的gist显示它比我之前使用的最佳方法快大约3倍。

英文:

As already mentioned, for quick efficient raw access to the underlying bits of a big.Int you want to use 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).
Even faster, you could use an assembly implementation of popcount that uses a native CPU instruction¹.

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):

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 &gt;&gt; 1) &amp; m1             //put count of each 2 bits into those 2 bits
	x = (x &amp; m2) + ((x &gt;&gt; 2) &amp; m2) //put count of each 4 bits into those 4 bits
	x = (x + (x &gt;&gt; 4)) &amp; m4        //put count of each 8 bits into those 8 bits
	return int((x * h01) &gt;&gt; 56)    //returns left 8 bits of x + (x&lt;&lt;8) + (x&lt;&lt;16) + (x&lt;&lt;24) + ...
}

Benchmarks and comparisons of this and other implementations presented here is available in full on GitHub gist.

¹ Such as the one added in Go1.9; the updated gist shows it is ~3× faster than the previous best I had.

答案2

得分: 4

你可以使用现在(从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))
}
英文:

You can use now (as of Go 1.9) the math/bits library that implements some useful functions that deal with bit-related computations. Concretely, you can iterate through the result of big.Int.Bits and call the bits.OnesCount function.

Here is an example:

package main

import (
	&quot;fmt&quot;
	&quot;math/big&quot;
	&quot;math/bits&quot;
)

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(&quot;%064b\n&quot;, x)
	}
}

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

	fmt.Println(&quot;Value in binary format:&quot;)
	PrintBinary(c)
	fmt.Println(&quot;BitCount:&quot;, BitCount(c))
}

答案3

得分: 3

我自己编写了一个函数,注意这个函数没有考虑数字的符号。它返回big.Int后面的原始字节的位数。

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

// 每个字节值(0-255)的位数。
var bitCounts = []int8{
    // 由Java BitCount生成的所有值从0到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,
}
英文:

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.

// 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,
}

答案4

得分: 2

以下是翻译好的内容:

FYI,以下解决方案比这里提供的原始解决方案更简单且更快:

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
英文:

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 &amp;= x-1
count++
}
}
return count
}

It outperforms the original solution by 5x on my machine:

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

huangapple
  • 本文由 发表于 2013年10月1日 07:57:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/19105791.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定