英文:
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 >> 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.
¹ 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 (
"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))
}
答案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 &= 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论