带符号大整数加减法,乘法,除法,模除,指数

一、问题的思考

1、对于大整数,我们只需要提供构造,显示,加减法,乘法,除法,模除,指数这些用户接口就可以了。

2、符号位怎么解决?首先考虑的办法是定义一个类,然后里面有sign_, std::vector<char> i_这两个成员变量,我在实现的过程中发现这样写代码,思路不够清晰,代码也不好写。因为当我们编写代码实现加减乘除,比较运算的时候,就不得不考虑符号位,这种设计会使代码的逻辑复杂,不够清晰。

3、正确的办法是先编写一个UnsignedInteger类实现所有的运算,然后再实现有符号的大整数运算。

4、如何实现无符号大整数加减乘除(减法只支持大减小)。加减都很简单,乘法只需提供基本UnsignedInteger * (0–9),UnsignedInteger * (10 ^K),然后利用加法运算,就可以实现乘法。除法则需要利用UnsignedInteger * (0–9),UnsignedInteger * (10 ^K)比较运算,减法运算,就可以实现除法。

5、无符号大整数的存储采用std::vector<char>,每一个char表示一个十进制位,并且第一个char表示最低位,最后一个char表示最高位

1、无符号整数的基础运算

代码如下:

#include <iostream>
#include <vector>
#include <assert.h>
#include <cmath>
#include <utility>

class UnsignBigInteger {
	private:
		std::vector< char > i_; 
		void compact() {
			int count = 0;
			for( int i = i_.size() - 1; i >= 0; i-- )
				if( i_[i] == 0 ) 
					count++;
				else
					break;
			//resize dose not change storage capacity directly
			i_.resize( i_.size() - count );
		}   
		void expand10( int k ) { 
			int curSize = i_.size();
			int expandSize = curSize + k;
			i_.resize( expandSize, 0 );
			copy_backward( i_.begin(), i_.begin() + curSize, 
					i_.end() );
			fill_n( i_.begin(), k, 0 );
		}   
		void add( const UnsignBigInteger &other ) { 
			int max = std::max( i_.size(), other.i_.size() );
			//to reserve one more place, so it is enough to hold sum    
			i_.resize( max + 1, 0 );  
			int c = 0;

			for( unsigned i = 0; i < i_.size(); i++ ) { 
				int value = i_[i] + c ; 
				if( i < other.i_.size() )
					value += other.i_[i];
				i_[i] = value % 10; 
				c = value / 10; 
			}   
		}  
		void sub( const UnsignBigInteger &o ) { 
			if( o.bigThan(*this) ) { 
				std::cout << "the other must small then current" << std::endl;
				return ;
			}
			int b = 0;
			for( unsigned i = 0; i < i_.size(); i++ ) {
				i_[i] -= b;
				if( i < o.i_.size() )
					i_[i] -= o.i_[i];

				if( i_[i] < 0 ) {
					i_[i] += 10;
					b = 1;
				} else {
					b = 0;
				}
			}
			compact();
		}
		void mul( int m ) {
			assert( m >= 0 && m < 10 );
			i_.resize( i_.size() + 1, 0 );
			int c = 0;
			for( unsigned i = 0; i < i_.size(); i++ ) {
				int value = i_[i] * m + c;
				i_[i] = value % 10;
				c = value / 10;
			}
		}
		const char & operator[]( int i ) const {
			return i_[i];
		}
		int size() const { return i_.size(); }
	public:
		bool bigThan( const UnsignBigInteger &o ) const {
			if( i_.size() != o.i_.size() )
				return i_.size() > o.i_.size();

			for( unsigned i = i_.size() - 1; i >= 0; i-- )
				if( i_[i] != o.i_[i] )
					return i_[i] > o.i_[i];

			return false;
		}
		void print(std::ostream &out) const {
			for( int i = i_.size() - 1; i >= 0; i-- )
				out << static_cast< int >( i_[i] );
		}

		//we must stop the implicit conversion
		explicit UnsignBigInteger( const std::string & nums ) :
			i_(nums.size(), 0){
				for( unsigned i = 0; i < nums.size(); i++ )
					i_[i_.size() - 1 - i] = nums[i] - '0';
			}

		explicit UnsignBigInteger( int n ) :
			i_(0){
				int v = n;

				while( v != 0 ) {
					i_.push_back( v % 10 );
					v /= 10;
				}
			}

		UnsignBigInteger() : i_(0) { }

		friend UnsignBigInteger& operator+=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend UnsignBigInteger& operator-=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend UnsignBigInteger operator-( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend UnsignBigInteger operator+( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend UnsignBigInteger operator*( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend std::ostream & operator<<( std::ostream &out, const UnsignBigInteger &b );
		friend UnsignBigInteger  power( const UnsignBigInteger &b, int e );
		friend std::pair<UnsignBigInteger, UnsignBigInteger> div( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs);
		friend UnsignBigInteger operator/( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
		friend UnsignBigInteger mod( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs );
};

该算法有一个非常重要的技巧是加法多预留一个字符,计算的时候就不会溢出,所有计算完compact, 这样就不会出问题

2、无符号整数运算的实现

UnsignBigInteger实现了大整数的基本运算,然后将需要的大整数运算定义为友元,下面是这些运算的实现

UnsignBigInteger& operator+=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	lhs.add( rhs );
	lhs.compact();
	return lhs;
}
UnsignBigInteger operator+( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	UnsignBigInteger tmp;
	tmp += lhs;
	tmp += rhs;
	tmp.compact();
	return tmp;
}
UnsignBigInteger& operator-=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	lhs.sub( rhs );
	return lhs;
}

UnsignBigInteger operator-( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	UnsignBigInteger ret( lhs );
	ret.sub( rhs );
	return ret;
}
UnsignBigInteger operator*( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	UnsignBigInteger ret;

	for( int i = 0; i < rhs.size(); i++ ) {
		UnsignBigInteger tmp( lhs );
		tmp.mul( rhs[i] );
		tmp.expand10( i );
		ret += tmp;
	}
	ret.compact();
	return ret;
}

std::pair<UnsignBigInteger, UnsignBigInteger> div( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs) {
	UnsignBigInteger u1(lhs), u2(rhs), ret;

	while( !u2.bigThan(u1) ) {
		unsigned s1 = u1.size(), s2 = u2.size();
		unsigned diff = s1 - s2;
		UnsignBigInteger temp(u2);
		temp.expand10(diff);

		if( temp.bigThan(u1) )
			diff--;

		UnsignBigInteger temp2(u2);
		temp2.expand10(diff);
		//i use a very naive way to calculate the most significant of the result
		int count = 0;
		while( !temp2.bigThan(u1) ) {
			u1 -= temp2;
			count++;
		}

		//accumulate the result
		UnsignBigInteger cur( count );
		cur.expand10( diff );
		ret += cur;
		u1.compact();
	}
	return std::pair<UnsignBigInteger, UnsignBigInteger>(ret, u1);

}
UnsignBigInteger operator/( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	return div( lhs, rhs ).first;
}

std::ostream & operator<<( std::ostream &out, const UnsignBigInteger &b ) {
	b.print( out );
	return out;
}

UnsignBigInteger mod( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) {
	return div( lhs, rhs ).second;
}
//tail recursion, how to eliminate?
UnsignBigInteger  power( const UnsignBigInteger &b, int e ) {

	if( e == 0 )
		return UnsignBigInteger( 1 );
	else if( (e & 1) == 1 )
		return b * power( b, e - 1 );
	else {
		UnsignBigInteger tmp = power( b, e / 2 );
		return tmp * tmp;
	}
}

3、有符号整数运算的实现

因为无符号的和有符号的大整数之间不是继承的关系,我倾向于组合,因此将无符号的大整数作为有符号的一个成员。然后需要做的就是基于无符号的大整数运算实现有符号的

利用运算的基本规则,有符号的:如果同号,符号为两个数的共同符号,如果不同号,则利用无符号的较大减去较小,然后符号位和较大的相同,其他运算和小学的运算规则一样

class SignBigInteger {
	private:
		bool sign_;
		UnsignBigInteger unbi_;
	public:
		SignBigInteger( const std::string &s, bool p = true) : sign_(p), unbi_(s) { };
		SignBigInteger(): sign_(true) { }
		friend SignBigInteger operator-( const SignBigInteger &s );
		friend SignBigInteger& operator+=( SignBigInteger &lhs, const SignBigInteger &rhs );
		friend SignBigInteger& operator-=( SignBigInteger &lhs, const SignBigInteger &rhs );
		friend SignBigInteger operator-( const SignBigInteger &lhs, const SignBigInteger &rhs );
		friend SignBigInteger operator+( const SignBigInteger &lhs, const SignBigInteger &rhs );
		friend SignBigInteger operator*( const SignBigInteger &lhs, const SignBigInteger &rhs );
		friend std::ostream & operator<<( std::ostream &out, const SignBigInteger &b );
		friend SignBigInteger  power( const SignBigInteger &b, int e );
		friend std::pair<SignBigInteger, SignBigInteger> div( const SignBigInteger &lhs, const SignBigInteger &rhs);
		friend SignBigInteger operator/( const SignBigInteger &lhs, const SignBigInteger &rhs );
		friend SignBigInteger mod( const SignBigInteger &lhs, const SignBigInteger &rhs );
};

SignBigInteger operator-( const SignBigInteger &s ) {
	SignBigInteger temp( s );
	temp.sign_ = !temp.sign_;
	return temp;
}
SignBigInteger& operator+=( SignBigInteger &lhs, const SignBigInteger &rhs ) {
	if( lhs.sign_ == rhs.sign_ ) {
		lhs.unbi_ += rhs.unbi_;
	} else {
		if( lhs.unbi_.bigThan( rhs.unbi_ ) ) {
			lhs.unbi_ -= rhs.unbi_;
		} else {
			lhs.unbi_ = rhs.unbi_ - lhs.unbi_;
			lhs.sign_ = rhs.sign_;
		}
	}
	return lhs;
}
SignBigInteger& operator-=( SignBigInteger &lhs, const SignBigInteger &rhs ) {
	return lhs += (- rhs);
}
SignBigInteger operator-( const SignBigInteger &lhs, const SignBigInteger &rhs ) {
	SignBigInteger temp(lhs);
	return temp -= rhs;
}
SignBigInteger operator+( const SignBigInteger &lhs, const SignBigInteger &rhs ) {
	SignBigInteger temp( lhs );
	return temp += rhs;
}
SignBigInteger operator*( const SignBigInteger &lhs, const SignBigInteger &rhs ) {
	SignBigInteger sb;
	if( lhs.sign_ == rhs.sign_ )
		sb.sign_ = true;
	else
		sb.sign_ = false;

	sb.unbi_ = lhs.unbi_ * rhs.unbi_;
	return sb;
}
std::ostream & operator<<( std::ostream &out, const SignBigInteger &b ) {
	if( b.sign_ == false )
		out << "-";
	out << b.unbi_;
	return out;
}
SignBigInteger  power( const SignBigInteger &b, int e ) {
	SignBigInteger sb;
	sb.sign_ = b.sign_;
	if( (b.sign_ == false) && ((e & 1) == 0) )
		sb.sign_ = true;

	sb.unbi_ = power( b.unbi_, e );
	return sb;
} 

当然,代码写到这里并没有结束,我们还需要实现无符号大整数和有符号大整数的相互转换,以及和其他一些数字的转换,不然支持的运算太有限了。

二、测试

最后需要做的就是测试,下面是测试代码:

int main() {
	UnsignBigInteger b1("12312345678901023456");
	UnsignBigInteger b2("4561233123123123123123");
	UnsignBigInteger b3(123);
	UnsignBigInteger b4(321);
	std::cout << b1 + b2 << std::endl;
	std::cout << b1 * b2 << std::endl;

	std::cout << b3 << std::endl;
	std::cout << b1 * b3 << std::endl;

	std::cout << power( b3, 2 ) << std::endl;
	std::cout << power( b3, 1100 ) << std::endl;

	std::cout << b4 - b3 << std::endl;
	std::cout << b2 - b1 << std::endl;

	std::cout << b4 / b3 << std::endl;
	std::cout << b2 / b1 << std::endl;
	std::cout << mod( b2, b1 ) << std::endl;
	std::cout << mod( b4, b3 ) << std::endl;


	SignBigInteger sb1("12345", false), sb2("123");

	std::cout << power(sb1, 20) << std::endl;
	std::cout << sb1 * sb2 << std::endl;
	return 0;
}

下面是执行结果,因为lisp里面支持大整数,我在lisp里面也执行相同的运算,结果是一样的

《带符号大整数加减法,乘法,除法,模除,指数》

扩展

实现整数到大整数,大整数到整数的转换,这样可以丰富运算的数据类型

采用std::vector<int>实现底层,速度至少可以提高8-9倍

    原文作者:大整数乘法问题
    原文地址: https://blog.csdn.net/yuan_da_xing/article/details/8207178
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞