高精度优化
wenzhenhao · · 算法·理论
引入
众所周知,高精度是一个十分耗内存的东西。我们要通过用 int 或 short 数组来储存每一位数字。
然而,short 能表示的范围是 bit 位算,一共需要 bit 位。然而 bit 位储存即可。让我们计算一下,存在了
就没有别的办法了吗?
准备工作
在解决问题之前,我们不得不准备一些东西。
是的,用 short 来储存十进制的数位显然是行不通的。那么,我们可以尝试用 bool 数组进行储存。此处示例使用 vector<bool>。
我们可以用泛型模板类的形式封装数字,并重载各种运算符,达到高精度的效果。
在此之前还需声明一下,所谓鱼和熊掌不可兼得。当你的数据范围增大时,使用
此处用到以下头文件:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>//当然你也可以使用万能头
逐步完成
一个好框架
既然是模板类,一个好的框架是必不可少的。下面是该类的框架,_value 是该数字的 bit 位数量。
template<size_t _value>
class BigNum {
//________
};
private 部分 变量定义&辅助函数
首先是变量,实际上不需要太多,仅仅需要储存一个 bool 数组即可。
vector<bool>bits;// 二进制位,bits[0]为最低位,bits[_value-1]为最高位
接下来是
// 辅助函数:十进制字符串转换为二进制
void fromDecimalString(const string& decStr) {
string current = decStr;
size_t pos = 0;
// 去除前导空格
size_t start = current.find_first_not_of(' ');
if (start != string::npos) {
current = current.substr(start);
}
// 检查有效数字
if (current.empty() ||
current.find_first_not_of("0123456789") != string::npos) {
throw invalid_argument("Invalid decimal string");
}
// 去除前导0
size_t nonZeroPos = current.find_first_not_of('0');
if (nonZeroPos == string::npos) {
fill(bits.begin(), bits.end(), false); // 全0
return;
}
current = current.substr(nonZeroPos);
// 模拟除2过程
while (!current.empty() && pos < _value) {
int remainder = 0;
string next;
for (char c : current) {
int digit = c - '0';
int num = remainder * 10 + digit;
int quot = num / 2;
remainder = num % 2;
if (!next.empty() || quot != 0) {
next.push_back(quot + '0');
}
}
bits[pos++] = (remainder == 1);
current = next;
}
}
// 辅助函数:二进制转十进制字符串
string toDecimalString() const {
string result = "0";
// 从最高位开始处理
for (int i = static_cast<int>(_value) - 1; i >= 0; i--) {
// 乘以2
int carry = 0;
for (int j = result.size() - 1; j >= 0; j--) {
int digit = result[j] - '0';
int temp = digit * 2 + carry;
carry = temp / 10;
result[j] = (temp % 10) + '0';
}
if (carry > 0) {
result.insert(result.begin(), carry + '0');
}
// 加当前位
if (bits[i]) {
int carryAdd = 1;
for (int j = result.size() - 1; j >= 0 && carryAdd > 0; j--) {
int digit = result[j] - '0';
int sum = digit + carryAdd;
carryAdd = sum / 10;
result[j] = (sum % 10) + '0';
}
if (carryAdd > 0) {
result.insert(result.begin(), carryAdd + '0');
}
}
}
// 去除前导0
size_t nonZero = result.find_first_not_of('0');
if (nonZero == string::npos) return "0";
return result.substr(nonZero);
}
由于我们要表示高精度,所以对转换的十进制以 string 字符串的形式保存。
public 部分
接下来就是一些公开函数。包括构造、重载与友元。
构造函数
构造函数的主要任务是初始化,下面展示了初始为 string 初始化和用 BigNum 类初始化的构造函数。
// 构造函数
BigNum() : bits(_value, false) {}
explicit BigNum(const string& decStr) : bits(_value, false) {
fromDecimalString(decStr);
}
BigNum(const BigNum& other) : bits(other.bits) {}
赋值
这是最基本的重载,只需要对 bits 进行复制即可。由于这里使用的是 vector ,所以可以直接赋值。如果你用的是数组,那就需要循环了。
BigNum& operator=(const BigNum& other) {
if (this != &other) {
bits = other.bits;
}
return *this;
}
位运算
位运算也算简单,对每一 bit 位进行一一比较即可。
BigNum operator&(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] & other.bits[i];
}
return result;
}
BigNum operator|(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] | other.bits[i];
}
return result;
}
BigNum operator^(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] ^ other.bits[i];
}
return result;
}
BigNum operator~() const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = !bits[i];
}
return result;
}
BigNum operator<<(size_t shift) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
if (i >= shift && i - shift < _value) {
result.bits[i] = bits[i - shift];
}
}
return result;
}
BigNum operator>>(size_t shift) const {
BigNum result;
for (int i = _value - 1; i >= 0; i--) {
if (i + shift < _value) {
result.bits[i] = bits[i + shift];
}
}
return result;
}
算术运算
算术运算算是比较难的一部分,需要在草稿纸上模拟几遍才能得出规律。由于篇幅限制,这里仅展示
BigNum operator+(const BigNum& other) const {
BigNum result;
bool carry = false;
for (size_t i = 0; i < _value; i++) {
bool a = bits[i];
bool b = other.bits[i];
result.bits[i] = a ^ b ^ carry;
carry = (a && b) || (a && carry) || (b && carry);
}
return result;
}
BigNum operator-(const BigNum& other) const {
BigNum result;
bool borrow = false;
for (size_t i = 0; i < _value; i++) {
int a = bits[i] ? 1 : 0;
int b = other.bits[i] ? 1 : 0;
int total = a - b - (borrow ? 1 : 0);
if (total < 0) {
total += 2;
borrow = true;
} else {
borrow = false;
}
result.bits[i] = total & 1;
}
return result;
}
BigNum operator*(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
if (other.bits[i]) {
BigNum temp = *this << i;
result = result + temp;
}
}
return result;
}
比较运算
类似于位运算,也是一一比较即可。
bool operator==(const BigNum& other) const {
for (size_t i = 0; i < _value; i++) {
if (bits[i] != other.bits[i]) {
return false;
}
}
return true;
}
bool operator!=(const BigNum& other) const {
return !(*this == other);
}
bool operator<(const BigNum& other) const {
for (int i = _value - 1; i >= 0; i--) {
if (bits[i] < other.bits[i]) return true;
if (bits[i] > other.bits[i]) return false;
}
return false;
}
bool operator<=(const BigNum& other) const {
return (*this < other) || (*this == other);
}
bool operator>(const BigNum& other) const {
return !(*this <= other);
}
友元函数
友元函数我们用于重载 cin 和 cout 。也就是说,我们要重载 ostream 和 istream ,这样也便于文件读写。
friend ostream& operator<<(ostream& os, const BigNum& num) {
os << num.toDecimalString();
return os;
}
friend istream& operator>>(istream& is, BigNum& num) {
string s;
is >> s;
num = BigNum(s);
return is;
}
完整代码
这里我们通过 sort 排序来检验这个类的可靠性。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>
using namespace std;
template <size_t _value>
class BigNum {
private:
vector<bool> bits; // 二进制位,bits[0]为最低位,bits[_value-1]为最高位
// 辅助函数:十进制字符串转换为二进制
void fromDecimalString(const string& decStr) {
string current = decStr;
size_t pos = 0;
// 去除前导空格
size_t start = current.find_first_not_of(' ');
if (start != string::npos) {
current = current.substr(start);
}
// 检查有效数字
if (current.empty() ||
current.find_first_not_of("0123456789") != string::npos) {
throw invalid_argument("Invalid decimal string");
}
// 去除前导0
size_t nonZeroPos = current.find_first_not_of('0');
if (nonZeroPos == string::npos) {
fill(bits.begin(), bits.end(), false); // 全0
return;
}
current = current.substr(nonZeroPos);
// 模拟除2过程
while (!current.empty() && pos < _value) {
int remainder = 0;
string next;
for (char c : current) {
int digit = c - '0';
int num = remainder * 10 + digit;
int quot = num / 2;
remainder = num % 2;
if (!next.empty() || quot != 0) {
next.push_back(quot + '0');
}
}
bits[pos++] = (remainder == 1);
current = next;
}
}
// 辅助函数:二进制转十进制字符串
string toDecimalString() const {
string result = "0";
// 从最高位开始处理
for (int i = static_cast<int>(_value) - 1; i >= 0; i--) {
// 乘以2
int carry = 0;
for (int j = result.size() - 1; j >= 0; j--) {
int digit = result[j] - '0';
int temp = digit * 2 + carry;
carry = temp / 10;
result[j] = (temp % 10) + '0';
}
if (carry > 0) {
result.insert(result.begin(), carry + '0');
}
// 加当前位
if (bits[i]) {
int carryAdd = 1;
for (int j = result.size() - 1; j >= 0 && carryAdd > 0; j--) {
int digit = result[j] - '0';
int sum = digit + carryAdd;
carryAdd = sum / 10;
result[j] = (sum % 10) + '0';
}
if (carryAdd > 0) {
result.insert(result.begin(), carryAdd + '0');
}
}
}
// 去除前导0
size_t nonZero = result.find_first_not_of('0');
if (nonZero == string::npos) return "0";
return result.substr(nonZero);
}
public:
// 构造函数
BigNum() : bits(_value, false) {}
explicit BigNum(const string& decStr) : bits(_value, false) {
fromDecimalString(decStr);
}
BigNum(const BigNum& other) : bits(other.bits) {}
// 赋值运算符
BigNum& operator=(const BigNum& other) {
if (this != &other) {
bits = other.bits;
}
return *this;
}
// 转换为十进制字符串
string toString() const {
return toDecimalString();
}
// 位运算
BigNum operator&(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] & other.bits[i];
}
return result;
}
BigNum operator|(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] | other.bits[i];
}
return result;
}
BigNum operator^(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = bits[i] ^ other.bits[i];
}
return result;
}
BigNum operator~() const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
result.bits[i] = !bits[i];
}
return result;
}
// 移位运算
BigNum operator<<(size_t shift) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
if (i >= shift && i - shift < _value) {
result.bits[i] = bits[i - shift];
}
}
return result;
}
BigNum operator>>(size_t shift) const {
BigNum result;
for (int i = _value - 1; i >= 0; i--) {
if (i + shift < _value) {
result.bits[i] = bits[i + shift];
}
}
return result;
}
// 算术运算
BigNum operator+(const BigNum& other) const {
BigNum result;
bool carry = false;
for (size_t i = 0; i < _value; i++) {
bool a = bits[i];
bool b = other.bits[i];
result.bits[i] = a ^ b ^ carry;
carry = (a && b) || (a && carry) || (b && carry);
}
return result;
}
BigNum operator-(const BigNum& other) const {
BigNum result;
bool borrow = false;
for (size_t i = 0; i < _value; i++) {
int a = bits[i] ? 1 : 0;
int b = other.bits[i] ? 1 : 0;
int total = a - b - (borrow ? 1 : 0);
if (total < 0) {
total += 2;
borrow = true;
} else {
borrow = false;
}
result.bits[i] = total & 1;
}
return result;
}
BigNum operator*(const BigNum& other) const {
BigNum result;
for (size_t i = 0; i < _value; i++) {
if (other.bits[i]) {
BigNum temp = *this << i;
result = result + temp;
}
}
return result;
}
// 比较运算符
bool operator==(const BigNum& other) const {
for (size_t i = 0; i < _value; i++) {
if (bits[i] != other.bits[i]) {
return false;
}
}
return true;
}
bool operator!=(const BigNum& other) const {
return !(*this == other);
}
bool operator<(const BigNum& other) const {
for (int i = _value - 1; i >= 0; i--) {
if (bits[i] < other.bits[i]) return true;
if (bits[i] > other.bits[i]) return false;
}
return false;
}
bool operator<=(const BigNum& other) const {
return (*this < other) || (*this == other);
}
bool operator>(const BigNum& other) const {
return !(*this <= other);
}
bool operator>=(const BigNum& other) const {
return !(*this < other);
}
// 输入输出流重载
friend ostream& operator<<(ostream& os, const BigNum& num) {
os << num.toDecimalString();
return os;
}
friend istream& operator>>(istream& is, BigNum& num) {
string s;
is >> s;
num = BigNum(s);
return is;
}
};
int main() {
int n;
cin >> n;
BigNum<64> a[n+1];
for(int i = 1;i <=n;i++)
cin>>a[i];
sort(a + 1,a + n + 1);
for(int i = 1;i <=n;i++)
cout<<a[i]<<" ";
return 0;
}
尾声
这篇文章的主要目的是为了让大家深刻了解高精度转二进制这种优化办法,希望对大家有帮助。别忘了 c++11!