C++大数类设计思路

洛谷大数类的评测结果(开了氧气优化)

BigNumber.png

这个第四个点真的优化不过去了QAQ,24W的数据,丧心病狂

整体构思

构造大数类名为 BigNumber ,首先想法是用字符串读入大数,然后将其转化为vector数组倒序分位存储的整数,然后通过一个 len 来记录数字的位数,便于做运算。还设计了一个标记变量,用于标记这数为正数还是负数。

构造函数

我用了两种构造函数,一个是无参构造函数,一个是拷贝构造函数,当然还有一个有参构造函数,但是实际过程中我没有用到。无参构造函数用于上述类成员的初始化,拷贝构造函数用于复制一个相同的大数类进行运算。有参构造函数可以用于对类成员的复制。

重载运算符

重载 “+”

  • 首先在类中进行了声明, BigNumber operator + (const BigNumber &b); ,用当前类 *this 来和引入的类 b 进行加法运算,返回值为一个 BigNumber 类。
  • 在类的外部进行重载的定义,先将用拷贝构造函数将当前的类 *this 拷贝为 a ,然后对 ab 进行加法运算,模拟竖式,对应位相加,大于10则进位,最后去掉尾部的 0 即可。
BigNumber BigNumber::operator + (const BigNumber &b) //重载 "+" 定义 
{
    
	BigNumber Result;
	BigNumber a(*this);
	Result.len = max(a.len,b.len)+1;
	int add=0;
	for(int i=0;i<Result.len||add!=0;i++)
	{
		int p=add;
		if(i<a.len) p+=a.v[i];
		if(i<b.len) p+=b.v[i];
		add=p/10;
		Result.v.push_back(p%10);
	}
	while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
	{
		Result.v.pop_back();
	}
	Result.len=Result.v.size();
	return Result;
    
}

重载 “<”

因为进行减法前需要比较两个大数类的大小,我就先重载了 < 。思路就是先比较a、b两个大数的长度,长的那个肯定比较大,如果两个长度相等。从尾部开始依次向前比较,如果不相等的话,就看两个数的相对大小,大的那个肯定整体比较大。如果总是相等,到了最后,就返回相应的值表示他们相等。这里我的返回值为int类型,用的标记是:如果两个相等,返回-1,如果前者小于后者,返回1,如果前者大于后者,返回0.

int  BigNumber::operator < (const BigNumber &b) // 重载 "<" 定义 
{
	BigNumber a(*this);
	
	if(a.len < b.len) return 1;
	if(a.len > b.len) return 0;
	
	for(int i=a.len-1;i>=0;i--)
	{
		if(a.v[i]!=b.v[i])
		{
			return a.v[i] < b.v[i];
		}
	}
	return -1;
}

重载 “-”

  • 首先在类中进行了声明, BigNumber operator - (BigNumber &b); ,用当前类 *this 来和引入的类 b 进行减法运算,返回值为一个 BigNumber 类。
  • 在类的外部进行重载的定义,先将用拷贝构造函数将当前的类 *this 拷贝为 a ,然后对 ab 进行减法运算。如果 a<b ,那么我们就把 flag 设为 true ,标志得数为一个负数,然后用 swap 交换两个类,保证总是大数减小数。减法的话就是从前往后扫,对应位相减,如果不够减的就进行借位。如果借完位当前位置小于0了,那么就再向前借位。最后要去掉结尾多于的0.
BigNumber BigNumber::operator - (BigNumber &b) // 重载 "-" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b==1)
	{
		swap(a,b);
		Result.flag=true;
	}
	Result.len=a.len;
	for(int i=0;i<a.len;i++)
	{
		if(a.v[i]<0)
		{
			a.v[i]+=10;
			a.v[i+1]--;
		} 
		if(a.v[i] < b.v[i]&&i<b.len)
		{
			a.v[i]+=10;
			a.v[i+1]--;
		}
		if(i<b.len)
		Result.v.push_back(a.v[i]-b.v[i]);
		else Result.v.push_back(a.v[i]);
	}
	while(Result.v[Result.len-1]==0 && Result.len > 1)
	{
		Result.v.pop_back();
		Result.len--;
	}
	return Result;
}

重载 “*”

  • 首先在类中进行了声明, BigNumber operator * (BigNumber &b);,用当前类 *this 来和引入的类 b 进行乘法运算,返回值为一个 BigNumber 类。
  • 在类的外部进行重载的定义,先将用拷贝构造函数将当前的类 *this 拷贝为 a ,然后对 ab 进行乘法运算。乘法也是模拟竖式运算,两个位数相乘对应的得数中的哪一位不难发现,因此只需要边乘边进位即可,一开始想的是所有乘完之后再进位,后来想了想运算的次序不会影响除和模的运算,所以就可以边乘边取商和模,可以少掉两层循环。算是一个小小的优化。最后要去掉结尾多于的0.
BigNumber BigNumber::operator * (BigNumber &b) // 重载 "*"  定义 
{
	BigNumber Result;
	BigNumber a(*this);
	Result.len=a.len+b.len;
	for(int i=0;i<Result.len;i++) Result.v.push_back(0);
	for(int i=0;i<a.len;i++)
	{
		for(int j=0;j<b.len;j++)
		{
			Result.v[i+j]+=a.v[i]*b.v[j];
			Result.v[i+j+1]+=Result.v[i+j]/10;
			Result.v[i+j]%=10;
		}
	}
	while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
	{
		Result.v.pop_back();
	}
	Result.len=Result.v.size();
	return Result;
}

重载 “/”

  • 首先在类中进行了声明,BigNumber operator / (BigNumber &b);,用当前类 *this 来和引入的类 b 进行除法运算,返回值为一个 BigNumber 类。

  • 在类的外部进行重载的定义,先将用拷贝构造函数将当前的类 *this 拷贝为 a ,然后对 ab 进行除法法运算。我选择了做除法的时候大数类中同时存储商和余数,这样可以加快效率,因为我自己的想法是把除法和取模一起处理,求得商的同时,模也能求出来。因此他们的框架肯定是相差无几的,所以我选择了一次性算出来两个,在大数类中用两个vector数组分别存商和余数,求商和余数我用的是减法的策略,分下面三种情况来讨论

    • a<b :很显然商为0,余数为a。
    • a==b :很显然商为1,余数为0。
    • a>b :这个是最难处理的,我们想想一下模拟除法的竖式运算,先在b的后面填0,让a和b的位数相同,然后再一直对a进行减法运算,将得到的数排列起来即可。里面细节还是挺多的,具体的看代码。最后要去掉结尾多于的0.
BigNumber BigNumber::operator / (BigNumber &b) // 重载 "/" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b==1)
	{
		Result.v.push_back(0);
		for(int i=0;i<a.v.size();i++)
		{
			Result.m.push_back(a.v[i]);
		}
		return Result;
	}
	if(a<b==-1)
	{
		Result.v.push_back(1);
		Result.m.push_back(0);
		Result.len=1;
	}
	if(a<b==0)
	{
		int size = a.len-b.len;
		for(int i=size;i>=0;i--)
		{
			BigNumber p(b);
			int cnt=0;
			for(int j=1;j<=i;j++)
			{
				p.v.insert(p.v.begin(),0);
			}
			p.len = p.v.size();
			while((a-p).flag==false)
			{
				a=a-p;
				cnt++;
			}
			if(i==size)
			{
				for(int j=1;j<=size;j++)
				{
					Result.v.push_back(0);
				}
				Result.v.push_back(cnt);
			}
			else
			{
				Result.v[i] = cnt;
			}
		}
		while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
		{
			Result.v.pop_back();
		}
		Result.len=Result.v.size();
		for(int i=0;i<a.v.size();i++)
		{
			Result.m.push_back(a.v[i]);
		}
	}
	return Result;
}

重载 “%”

这个和除法的类似,我们除法的其实已经求出来模了,这个只是象征性的搞一搞。QAQ。

BigNumber BigNumber::operator % (BigNumber &b) //重载 "%" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b)
	{
		Result.v.push_back(0);
		return Result;
	}
	else
	{
		int size = a.len-b.len;
		for(int i=size;i>=0;i--)
		{
			BigNumber p(b);
			for(int j=1;j<=i;j++)
			{
				p.v.insert(p.v.begin(),0);
			}
			p.len = p.v.size();
			while((a-p).flag==false)
			{
				a=a-p;
			}
		}
		for(int i=0;i<a.v.size();i++)
		{
			Result.v.push_back(a.v[i]);
		}
	}
	return Result;
} 

其中遇到的问题

  • 在做减法的时候,会出现莫名奇怪的数据,后来发现是由于访问b数组的时候越界,而且vector数组的clear只是将数组的size置为了0,而不是将所有数据都莫抹除,而且vector通过下标访问越界还不会报错,我整个人都傻了,调了巨长时间。
  • 在做除法的时候,一度自闭。本来是只是一直减,这样效率真的巨tm慢。后来想到了用这个办法好像可以优化到 log 级别的,但是好难调试啊。。从昨天下午一直搞到现在。
  • 因为swap这个东西,好像会影响到类的源数据,所以我就多定义了几个数,分别对他们进行操作,这样就不会相互影响,虽然看起来挺丑的。

整体的代码实现

/*
	Name: BigNumber Class
	Copyright: Zs
	Author: Zs
	Date: 04/04/20 09:08
	Description: A BigNumber Class
*/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>

using namespace std;

class BigNumber
{
	private:
		vector<int> v; // 和,差,积,商 
		vector<int> m; // 余数,当无余数的时候 size 为0 
		int len;  // 数字的长度 = v.size() 
		bool flag; //是否为负数的标志 
	public:
		BigNumber();//无参构造函数 
		BigNumber(string s)  //带参数的构造函数 
		{
			*this=s;
		}
		BigNumber(const BigNumber &); // 拷贝构造函数 
		BigNumber operator = (const string s);  // 重载 "=" 声明 
		BigNumber operator = (int k);  // 重载输入的数为整型的时候的 "=" 声明
		
		int operator < (const BigNumber &b);  // 重载 "<" 声明 
		BigNumber operator + (const BigNumber &b); // 重载 "+" 声明 
		BigNumber operator - (BigNumber &b); // 重载 "-" 声明 
		BigNumber operator * (BigNumber &b);  // 重载 "*" 声明 
		BigNumber operator / (BigNumber &b); //重载 "/" 声明 
		BigNumber operator % (BigNumber &b); //重载 "%" 声明 
		
		void print() //输出函数 
		{
			if(flag)
			{
				printf("-");
				flag=false;
			}
			for(int i=v.size()-1;i>=0;i--) printf("%d",v[i]);
			printf("\n");
			for(int i=m.size()-1;i>=0;i--) printf("%d",m[i]);
		}
		
};

BigNumber::BigNumber() //无参构造函数 
{
	v.clear();
	m.clear();
	len = 0;
	flag=false;
}
BigNumber::BigNumber(const BigNumber &T) // 拷贝构造函数 
{
	v.assign(T.v.begin(),T.v.end());
	len = T.len;
	flag = T.flag;
}

BigNumber BigNumber::operator = (const string s) // 重载 "=" 定义 
{
	len = s.length();
	for(int i=0;i<len;i++)
	{
		v.push_back(s[len-1-i]-'0');
	}
	return *this;
}
BigNumber BigNumber::operator = (int k) // 重载输入的数为整型的时候的 "=" 定义 
{
	while(k)
	{
		v.push_back(k%10);
		k/=10;
		len++; 
	}
	len--;
	return *this;
}

int  BigNumber::operator < (const BigNumber &b) // 重载 "<" 定义 
{
	BigNumber a(*this);
	
	if(a.len < b.len) return 1;
	if(a.len > b.len) return 0;
	
	for(int i=a.len-1;i>=0;i--)
	{
		if(a.v[i]!=b.v[i])
		{
			return a.v[i] < b.v[i];
		}
	}
	return -1;
}
BigNumber BigNumber::operator + (const BigNumber &b) //重载 "+" 定义 
{
	
	BigNumber Result;
	BigNumber a(*this);
	Result.len = max(a.len,b.len)+1;
	int add=0;
	for(int i=0;i<Result.len||add!=0;i++)
	{
		int p=add;
		if(i<a.len) p+=a.v[i];
		if(i<b.len) p+=b.v[i];
		add=p/10;
		Result.v.push_back(p%10);
	}
	while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
	{
		Result.v.pop_back();
	}
	Result.len=Result.v.size();
	return Result;
	
}
BigNumber BigNumber::operator - (BigNumber &b) // 重载 "-" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b==1)
	{
		swap(a,b);
		Result.flag=true;
	}
	Result.len=a.len;
	for(int i=0;i<a.len;i++)
	{
		if(a.v[i]<0)
		{
			a.v[i]+=10;
			a.v[i+1]--;
		} 
		if(a.v[i] < b.v[i]&&i<b.len)
		{
			a.v[i]+=10;
			a.v[i+1]--;
		}
		if(i<b.len)
		Result.v.push_back(a.v[i]-b.v[i]);
		else Result.v.push_back(a.v[i]);
	}
	while(Result.v[Result.len-1]==0 && Result.len > 1)
	{
		Result.v.pop_back();
		Result.len--;
	}
	return Result;
}

BigNumber BigNumber::operator * (BigNumber &b) // 重载 "*"  定义 
{
	BigNumber Result;
	BigNumber a(*this);
	Result.len=a.len+b.len;
	for(int i=0;i<Result.len;i++) Result.v.push_back(0);
	for(int i=0;i<a.len;i++)
	{
		for(int j=0;j<b.len;j++)
		{
			Result.v[i+j]+=a.v[i]*b.v[j];
			Result.v[i+j+1]+=Result.v[i+j]/10;
			Result.v[i+j]%=10;
		}
	}
	while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
	{
		Result.v.pop_back();
	}
	Result.len=Result.v.size();
	return Result;
}

BigNumber BigNumber::operator / (BigNumber &b) // 重载 "/" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b==1)
	{
		Result.v.push_back(0);
		for(int i=0;i<a.v.size();i++)
		{
			Result.m.push_back(a.v[i]);
		}
		return Result;
	}
	if(a<b==-1)
	{
		Result.v.push_back(1);
		Result.m.push_back(0);
		Result.len=1;
	}
	if(a<b==0)
	{
		int size = a.len-b.len;
		for(int i=size;i>=0;i--)
		{
			BigNumber p(b);
			int cnt=0;
			for(int j=1;j<=i;j++)
			{
				p.v.insert(p.v.begin(),0);
			}
			p.len = p.v.size();
			while((a-p).flag==false)
			{
				a=a-p;
				cnt++;
			}
			if(i==size)
			{
				for(int j=1;j<=size;j++)
				{
					Result.v.push_back(0);
				}
				Result.v.push_back(cnt);
			}
			else
			{
				Result.v[i] = cnt;
			}
		}
		while(Result.v[Result.v.size()-1]==0 && Result.v.size() > 1)
		{
			Result.v.pop_back();
		}
		Result.len=Result.v.size();
		for(int i=0;i<a.v.size();i++)
		{
			Result.m.push_back(a.v[i]);
		}
	}
	return Result;
}
BigNumber BigNumber::operator % (BigNumber &b) //重载 "%" 定义 
{
	BigNumber Result;
	BigNumber a(*this);
	if(a<b)
	{
		Result.v.push_back(0);
		return Result;
	}
	else
	{
		int size = a.len-b.len;
		for(int i=size;i>=0;i--)
		{
			BigNumber p(b);
			for(int j=1;j<=i;j++)
			{
				p.v.insert(p.v.begin(),0);
			}
			p.len = p.v.size();
			while((a-p).flag==false)
			{
				a=a-p;
			}
		}
		for(int i=0;i<a.v.size();i++)
		{
			Result.v.push_back(a.v[i]);
		}
	}
	return Result;
} 
int main()
{
//	freopen("test.in","r",stdin);
//	freopen("test.out","w",stdout);
	string s1,s2;
	cin>>s1>>s2;
	BigNumber a,b,c,e,f,k,j;
	a=s1,b=s2;
	e=s1,f=s2;
	k=s1,j=s2;
	c=a+b;
	c.print();
	c=a-b;
	c.print();
	c=k*j;
	c.print();
	c=e%f;
	c.print();
	return 0;
}