我正在尝试用C ++实现BigInteger类。但是,首先,我有一个基本问题,“基础数据”如何表示?例如,最愚蠢的方法是使用固定(或动态)char数组并在char中存储每个整数的整数。但是,好的,这是一种非常愚蠢的方式,我在这里是为了你的建议。
答案 0 :(得分:10)
现有实施有很多建议:C++ handling very large integers
如果你必须实施自己的(例如作业),那么你必须决定最好的方法,以及你需要处理的“大”。你可以使用一个DWORD数组,并处理从一个到下一个的溢出。
虽然,对于Project Euler的一些东西,我实际上实现了一个基于字符串构建的BigNumber类。事实证明,对于+ - * /来说,这是最简单的实现,并且缩放到比使用unsigned long long
s时更长的数字。并且表现完全足以解决这些难题。
因此,您需要在易于实施和最佳性能之间进行权衡。玩得开心; - )
答案 1 :(得分:5)
您可以完全按照描述的方式创建一个大整数。事实上,我第一次实现这样的课程,就像我做的那样。它帮助我实现了算术运算(+
,-
等),因为它在我习惯的基础(10)中。
对“字符数组”的自然增强是将其保持在基数10,但使用4位作为数字,而不是整个字节。因此,数字123,456可能由字节12 34 56
而不是字符串123456
表示。 (三个字节而不是六个字节。)
从那里,你可以为二号的数字存储。诸如加法的基本算术运算在基数2中与在基数10中完全相同。因此,可以使用字节FF FF
来存储数字65565。 (例如,在unsigned char
s的向量中。)BigInts的某些实现使用较大的块,例如short
或long
,以提高效率。
如果您要对base-10进行大量显示和/或序列化,并希望避免转换为base-2,那么Base-10 big int就非常有用。
答案 2 :(得分:0)
这是我的十进制基本实现。值的字符串和符号的布尔值(注意:注意,bool = true =>数字为负)。 几乎所有基本功能都已在这里测试并可以使用。
付款注意:逐位运算符的实现方式(sda-ka-dish)有所不同,所有操作均在 getBinary()函数中进行了解释。
把自己踢出去。
#include <string>
#ifndef AC1_INFINT_H
#define AC1_INFINT_H
class InfInt {
std::string value;
bool sign = 0;
public:
InfInt();
InfInt(const std::string&);
InfInt(long int);
InfInt operator=(long int);
explicit operator int() const;
std::string getValue() const;
bool getSign() const;
InfInt operator+(const InfInt&) const;
InfInt operator-(const InfInt&) const;
InfInt operator*(const InfInt&) const;
InfInt operator/(const InfInt&) const;
InfInt operator%(const InfInt&) const;
bool operator<(const InfInt&) const;
bool operator>(const InfInt&) const;
bool operator<=(const InfInt&) const;
bool operator>=(const InfInt&) const;
bool operator==(const InfInt&) const;
void operator+=(const InfInt&);
void operator-=(const InfInt&);
void operator++();
void operator--();
//void operator++();
//void operator--();
//BitWise
InfInt operator&(const InfInt&) const;
InfInt operator<<(const InfInt&) const;
InfInt operator>>(const InfInt& a) const;
void operator<<=(const InfInt&);
void operator>>=(const InfInt&);
void operator&=(const InfInt&);
InfInt operator^(const InfInt&) const;
InfInt operator|(const InfInt&) const;
unsigned long length() const;
std::string align(const unsigned long) const;
void initFromBinary(const std::string);
private:
std::string getBinary() const;
std::string subtract(const InfInt&) const;
std::string add(const InfInt&) const;
void setSign(const bool);
InfInt alignLeft(const unsigned long) const;
};
std::ostream& operator<<(std::ostream& os, const InfInt&);
#endif //AC1_INFINT_H
和cpp文件(如果您寻找特定的运算符,我想Ctrl + F会更好):
//
// Created by NSC on 11/7/18.
//
#include <string.h>
#include <cstring>
#include "LargeIntegers.h"
unsigned long InfInt::length() const{
if (value == "0")
return 0;
return value.length();
}
InfInt::InfInt(){
value = "0";
}
InfInt::InfInt(const std::string &s) {
unsigned long start = s.find_first_not_of("0-");
if (!std::strcmp(&s[start],"")) {
std::strcpy(&value[0], "0");
}
if(s[0] == '-') {
sign = 1;
}
value = &s[start];
}
InfInt::InfInt(long int a) {
sign =0;
if (a < 0){
sign = 1;
a *= -1;
}
value = std::to_string(a);
}
InfInt InfInt::operator=(long int a){
return *new InfInt(a);
}
std::string InfInt::getValue() const{
return value;
}
bool InfInt::getSign() const{
return sign;
}
void InfInt::setSign(const bool s){
sign = s;
}
InfInt::operator int() const{
int final = 0 ;
int pow = 1;
for (long i = length() - 1 ; i >= 0; i--) {
final += pow * (value[i] - '0');
pow*= 10;
}
if (sign)
final *= -1;
return final;
}
std::string InfInt::getBinary() const{
//if the number is 0
if (!length())
return "0";
InfInt temp = *new InfInt(value);
//sign at the start and then from lsb to msb
std::string result = "";
result = result + (char)((int)sign + '0');
std::string current;
while (temp > 0){
current= (temp%2).getValue();
if (current.length() == 0)
result = result + "0";
else
result = result + current;
temp = temp/2;
}
return result;
}
void InfInt::initFromBinary(const std::string b){
sign = b[0] - '0';
InfInt temp = 0;
InfInt shift = 1;
for (long i=1; i<b.length(); i++){
temp += *new InfInt((int)(b[i] - '0')) * shift;
shift = shift * 2;
}
value = temp.value;
}
std::ostream& operator<<(std::ostream& os, const InfInt& a){
if (a.length() == 0 || a.getValue()[0] == '0') {
std::string zero = "0";
return os << zero;
}
if (a.getSign())
return os << "-" + a.getValue();
else
return os<< a.getValue();
}
std::string InfInt::align(const unsigned long l) const{
std::string newStr = value;
for (unsigned long i=0; i<l; i++){
newStr = "0" + newStr;
}
return newStr;
}
InfInt InfInt::alignLeft(const unsigned long l ) const{
std::string newStr = value;
for (unsigned long i=0; i<l; i++){
newStr = newStr + "0";
}
return newStr;
}
void InfInt::operator++(){
*this+=1;
}
void InfInt::operator--(){
*this= *this - 1;
}
//adding the values of the input w/o their sign
std::string InfInt::add(const InfInt & a) const {
unsigned long numOfZeros = value.length() - a.length();
std::string aligned = a.align(numOfZeros);
//now this and a are in the same length
std::string result;
int x = 0;
bool carry = 0;
for (long i = value.length() -1; i>=0; i--){
//according to the ascii table '0' = 0 + 48 so subtracting 48 is a more sufficient way to convert
x=value[i] + aligned[i] - 96 + carry;
carry = x/10;
result = (char)(x%10 + 48) + result;
}
//in case of "overflow"
if (carry)
result = "1" + result;
return result;
}
InfInt InfInt::operator+(const InfInt & a) const{
//a + (-b) = a - b
if (!getSign() && a.getSign()) {
InfInt * temp = new InfInt(a.value);
return *this - *temp;
}
//(-a) + b = b - a
if (getSign() && !a.getSign()) {
//temp is positive. temp = -a
InfInt * temp = new InfInt(value);
return a - *temp;
}
//a + b = b + a
if (value.length() < a.length())
return a.operator+(*this);
std::string result = this->add(a);
//(-a) + (-b) = -(a + b)
if (getSign() && a.getSign())
return *new InfInt('-' + result);
return *new InfInt(result);
}
//when this function is called, we can be sure that a > b
std::string InfInt::subtract(const InfInt& a) const{
if (a.length() > length())
return a.subtract(*this);
long numOfZeros = this->length() - a.length();
std::string aligned = a.align(numOfZeros);
//now this and a are in the same length
std::string result = "";
int x = 0;
bool carry = 0;
for (long i = value.length() -1; i>=0; i--){
x=value[i] - aligned[i] - carry;
carry = false;
if (x < 0){
x+=10;
carry = true;
}
result = (char)(x + 48) + result;
}
return result;
}
InfInt InfInt::operator-(const InfInt &a) const{
if (length() == 0)
return a * *new InfInt(-1);
if (a.length() == 0)
return *this * *new InfInt(-1);
//both positive
if (!getSign() && !a.getSign()) {
if (*this > a)
return *new InfInt(this->subtract(a));
// a - b = -(b - a)
InfInt * result = new InfInt(a.subtract(*this));
result->setSign(true);
return *result;
}
//a - (-b) = a + b
if (!getSign() && a.getSign())
return *new InfInt(this->add(a));
//(-a) - b = - (a + b)
if (getSign() && !a.getSign()) {
InfInt result = this->add(a);
result.setSign(true);
return result;
}
//(-a) - (-b) = b - a
InfInt temp1 = *new InfInt(value);
InfInt temp2 = *new InfInt(a.value);
return temp2 - temp1;
// if (a > *this)
// return *new InfInt(a.subtract(*this));
//
// InfInt * result = new InfInt(this->subtract(a));
// result->setSign(true);
// return *result;
}
void InfInt::operator+=(const InfInt& a){
InfInt temp = *this + a;
value = temp.value;
sign = temp.sign;
}
void InfInt::operator-=(const InfInt& a){
InfInt temp = *this - a;
value = temp.value;
sign = temp.sign;
}
InfInt InfInt::operator*(const InfInt& a) const{
InfInt final = 0;
std::string result;
InfInt* temp;
int carry;
int current;
//fast mult algorithm. the same we were taught in elementary.
for(long i=length() - 1;i >= 0; i--){
carry = 0;
result = "";
for (long j=a.length() - 1; j >= 0; j--){
current = (value[i] - '0') * (a.value[j] - '0') + carry;
result = (char)(current % 10 + '0') + result;
carry = current / 10;
}
if (carry > 0)
result = (char)(carry + '0') + result;
temp = new InfInt(result);
final += *new InfInt(temp->alignLeft(length() - i - 1));
}
final.setSign(sign ^ a.sign);
return final;
}
//long division implementation
InfInt InfInt::operator/(const InfInt& a) const {
if (a.length() == 0 || a.getValue()[0] == '0') {
throw "Devision By Zero";
}
//divider = |a|
InfInt divider = *new InfInt(a.getValue());
if (divider > *new InfInt(value))
return *new InfInt();
std::string result;
int idx = 0;
//temp is the part of the divided that's being currently focused
InfInt temp = value[idx] - '0';
while (temp < divider.value)
temp = temp * 10 + (value[++idx] - '0');
while(idx < length()) {
if (temp == 0){
result = result + "0";
idx++;
}
else {
// Find prefix of number that's larger
// than a.value.
InfInt multNum = 1;
InfInt leftover = temp - divider;
while (leftover >= divider) {
leftover -= divider;
multNum += 1;
}
leftover = temp - (multNum * divider);
result = result + multNum.getValue();
temp = leftover * 10 + (value[++idx] - '0');
temp.setSign(false);
}
}
// If a.value is greater than value
if (result.length() == 0)
return *new InfInt();
InfInt final = *new InfInt(result);
final.setSign(this->sign ^ a.getSign());
return final;
}
InfInt InfInt::operator%(const InfInt& a) const {
if (a.length() == 0 || a.getValue()[0] == '0') {
throw "Modulo By Zero";
}
if (a > *this)
return *new InfInt(*this);
//divider = |a|
InfInt divider = *new InfInt(a.getValue());
if (divider > *new InfInt(value))
return (*this + a) % a;
std::string result;
int idx = 0;
InfInt leftover;
InfInt temp = value[idx] - '0';
while (temp < divider.value)
temp = temp * 10 + (value[++idx] - '0');
while(idx < length()) {
// Find prefix of number that's larger
// than a.value.
InfInt multNum = 0;
leftover = temp;
while (leftover >= divider) {
leftover -= divider;
multNum += 1;
}
leftover = temp - (multNum * divider);
leftover.setSign(false);
temp = leftover * 10 + (value[++idx] - '0');
}
// If a.value is greater than value
return leftover;
}
bool InfInt::operator<(const InfInt& a) const{
if (getSign() && !a.getSign())
return true;
if (!getSign() && a.getSign())
return false;
unsigned long l1 = length(), l2 = a. length();
//both positive
if (!getSign() && !a.getSign()) {
if (l1 > l2)
return false;
if (l1 < l2)
return true;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return false;
if (value[i] < a.value[i])
return true;
}
}
else {
if (l1 > l2)
return true;
if (l1 < l2)
return false;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return true;
if (value[i] < a.value[i])
return false;
}
}
//equal
return false;
}
bool InfInt::operator<=(const InfInt& a) const{
if (getSign() && !a.getSign())
return true;
if (!getSign() && a.getSign())
return false;
unsigned long l1 = length(), l2 = a. length();
//both positive
if (!getSign() && !a.getSign()) {
if (l1 > l2)
return false;
if (l1 < l2)
return true;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return false;
if (value[i] < a.value[i])
return true;
}
}
else {
if (l1 > l2)
return true;
if (l1 < l2)
return false;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return true;
if (value[i] < a.value[i])
return false;
}
}
//equal
return true;
}
bool InfInt::operator>(const InfInt& a) const{
if (getSign() && !a.getSign())
return false;
if (!getSign() && a.getSign())
return true;
unsigned long l1 = length(), l2 = a. length();
//both negative
if (getSign() && a.getSign()) {
if (l1 > l2)
return false;
if (l1 < l2)
return true;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return false;
if (value[i] < a.value[i])
return true;
}
}
else {
if (l1 > l2)
return true;
if (l1 < l2)
return false;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return true;
if (value[i] < a.value[i])
return false;
}
}
//equal
return false;
}
bool InfInt::operator>=(const InfInt& a) const{
if (getSign() && !a.getSign())
return false;
if (!getSign() && a.getSign())
return true;
unsigned long l1 = length(), l2 = a. length();
//both negative
if (getSign() && a.getSign()) {
if (l1 > l2)
return false;
if (l1 < l2)
return true;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return false;
if (value[i] < a.value[i])
return true;
}
}
else {
if (l1 > l2)
return true;
if (l1 < l2)
return false;
for (long i = 0; i < l1; i++) {
if (value[i] > a.value[i])
return true;
if (value[i] < a.value[i])
return false;
}
}
//equal
return true;
}
bool InfInt::operator==(const InfInt& a) const{
if (length() == 0 && a.length() == 0)
return true;
//if the signs are not the same
if (getSign() ^ a.getSign())
return false;
if (length() != a.length())
return false;
for(long i = 0; i < length(); i++){
if (value[i] != a.value[i])
return false;
}
return true;
}
void InfInt::operator<<=(const InfInt& a){
InfInt result = *this << a;
value = result.getValue();
sign = result.getSign();
}
void InfInt::operator>>=(const InfInt& a){
InfInt result = *this >> a;
value = result.getValue();
sign = result.getSign();
}
void InfInt::operator&=(const InfInt& a){
InfInt temp = *this & a;
value = temp.getValue();
sign = temp.getSign();
}
InfInt InfInt::operator^(const InfInt& a) const{
std::string b1 = this->getBinary();
std::string b2 = a.getBinary();
long l1 = b1.length();
long l2 = b2.length();
//adding zeros from right doesn't change the value of the number
int numOfZeros;
if (l1 > l2) {
numOfZeros = l1- l2;
for (int i=0; i < numOfZeros; i++)
b2 = b2 + "0";
}
else if (l2 > l1) {
numOfZeros = l2- l1;
for (int i=0; i < numOfZeros; i++)
b1 = b1 + "0";
}
std::string result = "";
for (int i = 0; i<l1+1; i++){
if (b1[i] == '1' ^ b2[i] == '1')
result = result + "1";
else
result = result + "0";
}
InfInt final = *new InfInt();
final.initFromBinary(result);
return final;
}
InfInt InfInt::operator|(const InfInt& a) const{
std::string b1 = this->getBinary();
std::string b2 = a.getBinary();
long l1 = b1.length();
long l2 = b2.length();
//adding zeros from right doesn't change the value of the number
int numOfZeros;
if (l1 > l2) {
numOfZeros = l1- l2;
for (int i=0; i < numOfZeros; i++)
b2 = b2 + "0";
}
else if (l2 > l1) {
numOfZeros = l2- l1;
for (int i=0; i < numOfZeros; i++)
b1 = b1 + "0";
}
std::string result = "";
for (int i = 0; i<l1; i++){
if (b1[i] == '1' || b2[i] == '1')
result = result + "1";
else
result = result + "0";
}
InfInt final = *new InfInt();
final.initFromBinary(result);
return final;
}
InfInt InfInt::operator&(const InfInt& a) const {
std::string b1 = this->getBinary();
std::string b2 = a.getBinary();
long l1 = b1.length();
long l2 = b2.length();
//adding zeros from right doesn't change the value of the number
int numOfZeros;
if (l1 > l2) {
numOfZeros = l1- l2;
for (int i=0; i < numOfZeros; i++)
b2 = b2 + "0";
}
else if (l2 > l1) {
numOfZeros = l2- l1;
for (int i=0; i < numOfZeros; i++)
b1 = b1 + "0";
}
std::string result = "";
for (int i = 0; i<l1; i++){
if (b1[i] == '1' && b2[i] == '1')
result = result + "1";
else
result = result + "0";
}
InfInt final = *new InfInt();
final.initFromBinary(result);
return final;
}
InfInt InfInt::operator<<(const InfInt& a) const {
std::string b = this->getBinary();
std::string result = "";
result = result + b[0];
//adding 0's multiplies by 2
for (InfInt i = 0; i < a; i+=1){
result = result + "0";
}
//adding the original bits after adding zeros
for (long j = 1 ; j < b.length(); j ++){
result = result + b[j];
}
InfInt final = *new InfInt();
final.initFromBinary(result);
return final;
}
InfInt InfInt::operator>>(const InfInt& a) const {
std::string b = this->getBinary();
b.erase(1, (int) a);
InfInt final = *new InfInt();
final.initFromBinary(b);
return final;
}