You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
4.3 KiB
138 lines
4.3 KiB
8 months ago
|
package cap3
|
||
|
|
||
|
import "strings"
|
||
|
|
||
|
// Ex19 高精度阶乘
|
||
|
func Ex19(n uint64) string {
|
||
|
result := []uint8{1}
|
||
|
for i := uint64(2); i <= n; i++ {
|
||
|
result = multiply(result, i)
|
||
|
}
|
||
|
|
||
|
return DigitSlice2String(result)
|
||
|
}
|
||
|
|
||
|
func multiply(a []uint8, b uint64) []uint8 {
|
||
|
result := make([]uint8, len(a)+22)
|
||
|
resultEndIdx := len(result) - 1
|
||
|
bOffset := 0
|
||
|
for i := b; i >= 1; i /= 10 {
|
||
|
carry := uint8(0)
|
||
|
bDigit := uint8(i % 10)
|
||
|
aEnd, resultIdx := len(a)-1, 0
|
||
|
for j := 0; j < len(a); j++ {
|
||
|
// 字符转数字
|
||
|
aDigit := a[aEnd-j]
|
||
|
// 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位
|
||
|
resultIdx = resultEndIdx - (bOffset + j)
|
||
|
num := result[resultIdx] + aDigit*bDigit + carry
|
||
|
// 当前位为计算结果的个位,进位为十位上的数字
|
||
|
result[resultIdx] = num % 10
|
||
|
carry = num / 10
|
||
|
}
|
||
|
|
||
|
// 如果还有进位,则对下一个结果位添加进位,此时不会再有进位
|
||
|
if carry != 0 {
|
||
|
resultIdx--
|
||
|
result[resultIdx] += carry
|
||
|
}
|
||
|
|
||
|
bOffset++
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func multiply2(a []uint8, b uint64) []uint8 {
|
||
|
result := make([]uint8, len(a)+22)
|
||
|
resultEndIdx := len(result)*2 - 1
|
||
|
bOffset := 0
|
||
|
for i := b; i >= 1; i /= 10 {
|
||
|
carry := uint8(0)
|
||
|
bDigit := uint8(i % 10)
|
||
|
aEnd, resultIdx := len(a)*2-1, 0
|
||
|
for j := 0; j < len(a); j++ {
|
||
|
// 字符转数字
|
||
|
aDigit := uint8(0)
|
||
|
// 4,5 -> 37(0010,0101)
|
||
|
aIdx := aEnd - j
|
||
|
if aIdx%2 == 0 {
|
||
|
aDigit = a[aIdx/2] >> 4
|
||
|
} else {
|
||
|
aDigit = a[aIdx/2] & 0b00001111
|
||
|
}
|
||
|
|
||
|
// 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位
|
||
|
resultIdx = resultEndIdx - (bOffset + j)
|
||
|
bitIdx := resultIdx / 2
|
||
|
if resultIdx%2 == 0 {
|
||
|
num := (result[bitIdx] >> 4) + aDigit*bDigit + carry
|
||
|
// 清位
|
||
|
// 4,5 -> 37(0010,0101) => 0,5 -> 5(0000,0101)
|
||
|
result[bitIdx] &= 0b00001111
|
||
|
// 位赋值
|
||
|
// 0,5 -> 5(0000,0101) => 5,5 -> 5(0101,0101)
|
||
|
// 5(0000,0101) << 4 => 80(0101,0000)
|
||
|
// 5(0000,0101) | 80(0101,0000) => 5,5 -> 5(0101,0101)
|
||
|
result[bitIdx] |= (num % 10) << 4
|
||
|
carry = num / 10
|
||
|
} else {
|
||
|
num := (result[bitIdx] & 0b00001111) + aDigit*bDigit + carry
|
||
|
// 清位
|
||
|
// 4,5 -> 37(0010,0101) => 4,0 -> 32(0010,0000)
|
||
|
result[bitIdx] &= 0b11110000
|
||
|
// 赋值
|
||
|
// 4,0 -> 32(0010,0000) => 4,5 -> 37(0010,0101)
|
||
|
// 32(0010,0000) | 5(0000,0101) => 4,5 -> 37(0010,0101)
|
||
|
result[bitIdx] |= num % 10
|
||
|
carry = num / 10
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 如果还有进位,则对下一个结果位添加进位,此时不会再有进位
|
||
|
if carry != 0 {
|
||
|
resultIdx--
|
||
|
bitIdx := resultIdx / 2
|
||
|
if resultIdx%2 == 0 {
|
||
|
// 4,5 -> 37(0010,0101) => 5,5 -> 37(0101,0101)
|
||
|
// 4+1 -> 5(0000,0101) << 4 => 80(0101,0000)
|
||
|
// 5(0000,0101) | 80(0101,0000) => 5,5 -> 5(0101,0101)
|
||
|
result[bitIdx] &= 0b00001111
|
||
|
num := (result[bitIdx] >> 4) + carry
|
||
|
result[bitIdx] |= num << 4
|
||
|
} else {
|
||
|
result[bitIdx] &= 0b11110000
|
||
|
num := (result[bitIdx] & 0b00001111) + carry
|
||
|
result[bitIdx] |= num
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bOffset++
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Ex19LowMem 低内存使用版,两位数字结果按位存在同一个uint8中,一个0~9数字只使用四字节,uint8可存两位,一个字节拆成两半用
|
||
|
func Ex19LowMem(n uint64) string {
|
||
|
result := []uint8{1}
|
||
|
for i := uint64(2); i <= n; i++ {
|
||
|
result = multiply2(result, i)
|
||
|
}
|
||
|
|
||
|
zeroPrefix := true
|
||
|
sb := strings.Builder{}
|
||
|
for _, num := range result {
|
||
|
if zeroPrefix && num == 0 {
|
||
|
continue
|
||
|
} else {
|
||
|
zeroPrefix = false
|
||
|
}
|
||
|
|
||
|
sb.WriteByte((num >> 4) + 48)
|
||
|
sb.WriteByte((num & 0b00001111) + 48)
|
||
|
}
|
||
|
|
||
|
return sb.String()
|
||
|
}
|