化学方程式是化学反应简明的表达形式,它从“质”和“量”两个方面表达了化学反应的意义。故化学方程式的书写是我们学习化学的过程中不可或缺的一个重要环节。当我们遇到简单的化学方程式例如:2H2 + O2 = 2H2O 时,配平则是毫无压力,但是若遇到类似Fe36Si5 + H3PO4 + K2CrO7 = FePO4 + SiO2 + K3PO4+ CrPO4 + H2O 这种元素种类繁多,化学计量数复杂的化学方程式,若仅依靠人力去配平可能是一件极为困难的事情。而计算机具有庞大的计算能力,故我们想到编写程序来解决复杂化学方程的配平。本程序将实现化学方程式和离子方程式的配平,并支持检测输入的方程式是否配平。
主要思路:
先做字符串处理,把每个物质的的每种原子数都找出来,
然后利用每种原子的守恒 关于系数 列出方程组 进行求解 (化合价好像不太现实,我化学不好)
先说方程的解法,
解线性方程组当然是要用高斯消元了。
/*
Chemical Equation Balancer
HiJ1m 2017.10.6
*/
#include
using namespace std;
inline int gcd(int x,int y){
return x%y==0?y:gcd(y,x%y);
}
inline int lcm(int x,int y){
return x*y/gcd(x,y);
}
struct frac{ //分数类
int a,b;
void reduce(){
int x=gcd(a,b);
a/=x,b/=x;
};
frac operator = (int x){
a=x,b=1;
return *this;
};
frac operator = (const frac x){
a=x.a,b=x.b;
reduce();
return *this;
};
frac operator + (const frac x){
return (frac){b*x.a+a*x.b,b*x.b};
};
frac operator - (const frac x){
return (frac){a*x.b-b*x.a,b*x.b};
};
frac operator * (const frac x){
return (frac){a*x.a,b*x.b};
};
frac operator / (const frac x){
return (frac){a*x.b,b*x.a};
};
bool operator < (const frac x){
return a*x.b<b*x.a;
};
bool operator == (const frac x){
return a*x.b==b*x.a;
};
void print(){
if(b==1)printf("%d\n",a);
else printf("%d/%d\n",a,b);
};
};
inline frac Abs(frac x){
int p=x.a>0?x.a:-x.a,q=x.b>0?x.b:-x.b;
return (frac){p,q};
}
char s[55];
int fun[55][55];
int Map[27][27]; //手动MAP
frac M[55][55]; //求解矩阵
frac ans[55]; //解
int Ans[55]; //整数解
int cnt,c1,c2,flag=1,N,K; //cnt数元素,c1数反应物,c2总数 (未知数的数量)
char mat[55][55]; //存储物质的名称
void print(){
printf("%d %d\n",N,K);
for(int i=1;i<=K;i++){
for(int j=1;j<=N+1;j++)
printf("%d ",M[i][j].a);
printf("\n");
}
printf("\n");
}
inline int getint(int pos){ //读数
pos++;
if(s[pos]>='a'&&s[pos]<='z')pos++;
if(s[pos]<'0'||s[pos]>'9')return 1; //没数就是1
else {
int x=0;
while(s[pos]>='0'&&s[pos]<='9')x=x*10+s[pos]-'0',pos++; //读元素后面的数字
return x;
}
}
inline void scan(int l,int r){ //处理物质
c2++;
for(int i=0;i<=r-l;i++)mat[c2][i]=s[l+i]; //存下元素的名字
if(flag==1)c1++; //统计一下反应物数量
int tmp=1; //tmp是小括号倍数
for(int i=l;i<=r;i++){
if(s[i]==')')tmp=1;
if(s[i]=='('){
int j=i+1;while(s[j]!=')')j++; //找这个括号的范围
tmp=getint(j); //读")"右边的数字
}
if(s[i]>='A'&&s[i]<='Z'){ //发现元素
int x=s[i]-'A'+1,y=0;
if(s[i+1]>='a'&&s[i]<='z') //看一眼是一个字母的还是两个的
y=s[i+1]-'a'+1;
if(!Map[x][y])Map[x][y]=++cnt; //判重
fun[Map[x][y]][c2]+=flag*getint(i)*tmp; //把这个物质里的这种元素数量放进矩阵里,坐标(map[x][y],c2)
}
}
}
inline bool Solve(){ //解方程 (矩阵 高cnt,宽c2+1,c2+1列常数全0)
ans[c2]=1; //令最后一个解为1
for(int i=1;i<=cnt;i++){
for(int j=1;j<=c2;j++)
M[i][j]=fun[i][j];
}
for(int i=1;i<=cnt;i++)
M[i][c2].a=-M[i][c2].a; //移到常数
//高斯消元过程
N=c2-1,K=cnt;
for(int k=1;k<=N;k++){
frac maxm=(frac){-1,1};int maxi;
for(int i=k;i<=K;i++)
if(maxm<Abs(M[i][k]))
maxm=Abs(M[i][k]),maxi=i;
if(maxm==(frac){0,1})
return false;
if(maxi!=k)
for(int j=1;j<=N+1;j++){
swap(M[k][j],M[maxi][j]);
}
frac tmp=M[k][k];
for(int j=1;j<=N+1;j++)
M[k][j]=M[k][j]/tmp;
for(int i=k-1?1:2;i<=K;i++){
if(i==k)continue;
frac tmp=M[i][k];
for(int j=1;j<=N+1;j++)
M[i][j]=M[i][j]-tmp*M[k][j];
}
}
return true;
}
int main()
{
// printf("Chemical Equation Balancer\n");
// printf("\nEnter the chemical equation:\n");
scanf("%s",s);
int lst=0;
for(int i=1;i<strlen(s);i++){
if(i==strlen(s)-1)scan(lst,i);
if(s[i]=='+'||s[i]=='=')scan(lst,i-1),lst=i+1;
if(s[i]=='=')flag=-1; //等号后面的系数变负
}
if(Solve())
for(int i=1;i<=c2-1;i++)
ans[i]=M[i][N+1];
else printf("No Solution");
int tmp=lcm(ans[1].b,ans[2].b);
for(int i=3;i<=c2;i++)tmp=lcm(tmp,ans[i].b);
for(int i=1;i<=c2;i++)Ans[i]=ans[i].a*tmp/ans[i].b; //取分母Lcm,把分数变整数
for(int i=1;i<=c2;i++)
{
if(Ans[i]>1)printf("%d",Ans[i]);
for(int j=0;j<strlen(mat[i]);j++)
printf("%c",mat[i][j]);
if(i==c2)return 0;
else if(i==c1)printf("=");
else printf("+");
}
}
另一个代码:
/* Exzh Cross Platfrom Toolkit (ECPT) Qt Version
* (This file is the part of the ECPT Project)
* Author: Exzh_PMGI
* E-mail: [email protected]
* License: LGPL v3.0 / Exzh Commerical License
* Copyright: (C) Exzh_PMGI
* Qt Framework 5.10 has been tested successfully
* If you want to use the code for business,
* please contact me by my email.
*/
#include "exequationbalancer.h"
#include
int gcd(int x, int y) {
return x % y == 0 ? y : gcd(y, x%y);
}
int lcm(int x, int y) {
return x * y / gcd(x, y);
}
frac createFrac(int a, int b)
{
frac tmp = { a,b };
return tmp;
}
frac Abs(frac x) {
int p = x.a>0 ? x.a : -x.a, q = x.b>0 ? x.b : -x.b;
return createFrac(p, q);
}
string exEquationBalancer::getResult(string inputstr)
{
strcpy(s,inputstr.c_str());
int lst = 0;
for (int i = 1;i<strlen(s);i++) {
if (i == strlen(s) - 1)scan(lst, i);
if (s[i] == '+' || s[i] == '=')
{
scan(lst, i - 1);
lst = i + 1;
}
if (s[i] == '=')flag = -1; //等号后面的系数变负
}
if (Solve())
for (int i = 1;i <= c2 - 1;i++)
ans[i] = M[i][N + 1];
else output+="No Solution";
int tmp = lcm(ans[1].b, ans[2].b);
for (int i = 3;i <= c2;i++)tmp = lcm(tmp, ans[i].b);
for (int i = 1;i <= c2;i++)Ans[i] = ans[i].a*tmp / ans[i].b; //取分母Lcm,把分数变整数
for (int i = 1;i <= c2;i++)
{
if (Ans[i]>1) output+=to_string(Ans[i]);
for (int j = 0;j<strlen(mat[i]);j++)
output+=mat[i][j];
if (i == c2)
{
return output;
qDebug()<<QString::fromStdString(output);
}
else if (i == c1) output+="=";
else output+="+";
}
}
bool exEquationBalancer::Solve() { //解方程 (矩阵 高cnt,宽c2+1,c2+1列常数全0)
ans[c2] = 1; //令最后一个解为1
for (int i = 1;i <= cnt;i++) {
for (int j = 1;j <= c2;j++)
M[i][j] = fun[i][j];
}
for (int i = 1;i <= cnt;i++)
M[i][c2].a = -M[i][c2].a; //移到常数
//高斯消元过程
N = c2 - 1, K = cnt;
for (int k = 1;k <= N;k++) {
frac maxm = createFrac(-1, 1);
int maxi;
for (int i = k;i <= K;i++)
if (maxm<Abs(M[i][k]))
maxm = Abs(M[i][k]), maxi = i;
if (maxm == createFrac(0, 1))
return false;
if (maxi != k)
for (int j = 1;j <= N + 1;j++) {
swap(M[k][j], M[maxi][j]);
}
frac tmp = M[k][k];
for (int j = 1;j <= N + 1;j++)
M[k][j] = M[k][j] / tmp;
for (int i = k - 1 ? 1 : 2;i <= K;i++) {
if (i == k)continue;
frac tmp = M[i][k];
for (int j = 1;j <= N + 1;j++)
M[i][j] = M[i][j] - tmp * M[k][j];
}
}
return true;
}
void exEquationBalancer::scan(int l, int r) { //处理物质
c2++;
for (int i = 0;i <= r - l;i++)mat[c2][i] = s[l + i]; //存下元素的名字
if (flag == 1)c1++; //统计一下反应物数量
int tmp = 1; //tmp是小括号倍数
for (int i = l;i <= r;i++) {
if (s[i] == ')')tmp = 1;
if (s[i] == '(') {
int j = i + 1;while (s[j] != ')')j++; //找这个括号的范围
tmp = getint(j); //读")"右边的数字
}
if (s[i] >= 'A'&&s[i] <= 'Z') { //发现元素
int x = s[i] - 'A' + 1, y = 0;
if (s[i + 1] >= 'a'&&s[i] <= 'z') //看一眼是一个字母的还是两个的
y = s[i + 1] - 'a' + 1;
if (!Map[x][y])Map[x][y] = ++cnt; //判重
fun[Map[x][y]][c2] += flag * getint(i)*tmp; //把这个物质里的这种元素数量放进矩阵里,坐标(map[x][y],c2)
}
}
}
int exEquationBalancer::getint(int pos) { //读数
pos++;
if (s[pos] >= 'a'&&s[pos] <= 'z')pos++;
if (s[pos]<'0' || s[pos]>'9')return 1; //没数就是1
else {
int x = 0;
while (s[pos] >= '0'&&s[pos] <= '9')x = x * 10 + s[pos] - '0', pos++; //读元素后面的数字
return x;
}
}
void exEquationBalancer::print() {
output += to_string(N);
output += " ";
output += to_string(K);
output += "\n";
for (int i = 1;i <= K;i++) {
for (int j = 1;j <= N + 1;j++)
{
output += to_string(M[i][j].a);
output += " ";
}
output += "\n";
}
output += "\n";
}
c++头文件
/* Exzh Cross Platfrom Toolkit (ECPT) Qt Version
* (This file is the part of the ECPT Project)
* Author: Exzh_PMGI
* E-mail: [email protected]
* License: LGPL v3.0 / Exzh Commerical License
* Copyright: (C) Exzh_PMGI
* Qt Framework 5.10 has been tested successfully
* If you want to use the code for business,
* please contact me by my email.
*/
#ifndef EXEQUATIONBALANCER_H
#define EXEQUATIONBALANCER_H
#include
#include "../exstdc++.h"
using namespace std;
static string output;
int lcm(int x, int y);
int gcd(int x, int y);
struct frac { //分数类
int a, b;
void reduce() {
int x = gcd(a, b);
a /= x, b /= x;
}
frac createFrac(int a, int b)
{
frac tmp = { a,b };
return tmp;
}
frac operator = (int x) {
a = x, b = 1;
return *this;
}
frac operator = (const frac x) {
a = x.a, b = x.b;
reduce();
return *this;
}
frac operator + (const frac x) {
return createFrac(b*x.a + a * x.b, b*x.b);
}
frac operator - (const frac x) {
return createFrac(a*x.b - b * x.a, b*x.b);
}
frac operator * (const frac x) {
return createFrac(a*x.a, b*x.b);
}
frac operator / (const frac x) {
return createFrac(a*x.b, b*x.a);
}
bool operator < (const frac x) {
return a * x.b<b*x.a;
}
bool operator == (const frac x) {
return a * x.b == b * x.a;
}
void print() {
if (b == 1)
{
output += to_string(a);
output += "\n";
}
else
{
output += to_string(a);
output += "/";
output += to_string(b);
}
}
};
frac createFrac(int a, int b);
frac Abs(frac x);
class exEquationBalancer
{
public:
string getResult(string inputstr);
private:
bool Solve();
void scan(int l, int r);
int getint(int pos);
void print();
char s[55];
int fun[55][55];
int Map[27][27]; //手动MAP
frac M[55][55]; //求解矩阵
frac ans[55]; //解
int Ans[55]; //整数解
int cnt, c1, c2, flag = 1, N, K; //cnt数元素,c1数反应物,c2总数 (未知数的数量)
char mat[55][55]; //存储物质的名称
};
#endif // EXEQUATIONBALANCER_H
附录:
怎样用高斯消元 方法/步骤
下面的是咱们要求解的线性方程组,先把四个方程编上序号。
先把第一行乘以1/2,然后把第一行的相应倍数加到第二、三、四行上。
再把第二行乘以-2,接着将其相应的倍数加到第三、四行上,然后把第三行乘以-1/6。
将第三行的二倍加到第四行上,再把第四行乘以3/7。
然后往回代,就是把第四行的相应倍数加到第一、第二、第三行上;把第三行的相应倍数加到第一、第二行上。
再把第二行的相应倍数加到第一行上,最后根据得出的矩阵列出方程组,解得最后的解。