diff --git a/.idea/chemical_equation_balancer.iml b/.idea/chemical_equation_balancer.iml
index 8e5446a..4bad9f7 100644
--- a/.idea/chemical_equation_balancer.iml
+++ b/.idea/chemical_equation_balancer.iml
@@ -10,5 +10,6 @@
+
\ No newline at end of file
diff --git a/.idea/other.xml b/.idea/other.xml
new file mode 100644
index 0000000..640fd80
--- /dev/null
+++ b/.idea/other.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/equation_solver.py b/equation_solver.py
index ffc7003..c1683f7 100644
--- a/equation_solver.py
+++ b/equation_solver.py
@@ -1,3 +1,9 @@
+from fractions import Fraction
+
+import numpy as np
+import scipy as sp
+
+
def solve_equation(eq):
"""
配平化学方程式,返回配平后的化学方程式
@@ -15,12 +21,94 @@ def solve_equation(eq):
:return: 配平后的化学方程式,与输入格式相同
若无法配平,则返回 None
"""
- # 使用加减消元法解方程组
- # 依据:反应前后,每个元素的个数相等
-
- # 用二维数组表示方程组的系数,用一维数组表示方程组的常数项
-
+ # 统计所有元素的种类
+ elements = set()
+ for each in eq['left']:
+ for atom in each['atoms']:
+ elements.add(list(atom.keys())[0])
+ for each in eq['right']:
+ for atom in each['atoms']:
+ elements.add(list(atom.keys())[0])
+ elements = list(elements)
+ # 构造系数矩阵
+ matrix = []
+ constant = []
+ for atom in elements:
+ # 遍历左边,左侧系数为正
+ row = []
+ for each in eq['left']:
+ for atom_ in each['atoms']:
+ if atom in atom_:
+ row.append(atom_[atom])
+ break
+ else:
+ row.append(0)
+ # 遍历右边,右侧系数为负
+ for each in eq['right']:
+ for atom_ in each['atoms']:
+ if atom in atom_:
+ row.append(-atom_[atom])
+ break
+ else:
+ row.append(0)
+ # 取出row中的最后一个元素,反转后加入常数项
+ constant.append(-row.pop())
+ matrix = np.mat(matrix, int)
+ print(matrix)
+ print(constant)
+ # 求解线性方程组
+ try:
+ result = sp.linalg.solve(matrix, constant).tolist()
+ except Exception as e:
+ print(e.args[0])
+ print('无解')
+ return None
+ # 将结果写入化学方程式,最后一个生成物的系数需要计算得到
+ last_substance = eq['right'][-1]
+ last_atom = list(last_substance['atoms'][0].keys())[0]
+ # 计算除最后一种生成物外的所有生成物包含last_atom的系数之和
+ sum_ = 0
+ index = 0
+ for each in eq['left']:
+ for atom in each['atoms']:
+ if last_atom in atom:
+ # 该生成物包含last_atom,则获得last_atom的总个数,等于系数*原子个数
+ sum_ += result[index] * atom[last_atom]
+ break
+ index += 1
+ for each in eq['right'][:-1]:
+ for atom in each['atoms']:
+ if last_atom in atom:
+ sum_ -= result[index] * atom[last_atom]
+ break
+ index += 1
+ result.append(sum_ / last_substance['atoms'][0][last_atom])
+ result = expand_to_int(*result)
+ for i, each in enumerate(eq['left']):
+ each['coefficient'] = result[i]
+ for i, each in enumerate(eq['right']):
+ each['coefficient'] = result[i + len(eq['left'])]
+ return eq
+def expand_to_int(*args):
+ """
+ 将一系列小数同时扩大,全部转换为最接近的整数
+ :param args: 一系列小数
+ :return: 一系列整数
+ """
+ # 将所有小数转换为分数
+ fractions = []
+ for each in args:
+ fractions.append(Fraction(each).limit_denominator())
+ # 计算所有分数的最小公倍数
+ lcm = 1
+ for each in fractions:
+ lcm = lcm * each.denominator // np.gcd(lcm, each.denominator)
+ # 将所有分数扩大为最小公倍数
+ result = []
+ for each in fractions:
+ result.append(each.numerator * lcm // each.denominator)
+ return result
\ No newline at end of file
diff --git a/fraction.py b/fraction.py
deleted file mode 100644
index ef36fdb..0000000
--- a/fraction.py
+++ /dev/null
@@ -1,117 +0,0 @@
-class Fraction:
- """
- 代表分数的类,包含分子和分母
- 提供加减乘除运算
- """
- def __init__(self, top, bottom):
- self.num = top
- self.den = bottom
-
- def __str__(self):
- return str(self.num) + "/" + str(self.den)
-
- def __add__(self, otherfraction):
- newnum = self.num * otherfraction.den + self.den * otherfraction.num
- newden = self.den * otherfraction.den
- return Fraction(newnum, newden)
-
- def __sub__(self, otherfraction):
- newnum = self.num * otherfraction.den - self.den * otherfraction.num
- newden = self.den * otherfraction.den
- return Fraction(newnum, newden)
-
- def __mul__(self, otherfraction):
- newnum = self.num * otherfraction.num
- newden = self.den * otherfraction.den
- return Fraction(newnum, newden)
-
- def __truediv__(self, otherfraction):
- newnum = self.num * otherfraction.den
- newden = self.den * otherfraction.num
- return Fraction(newnum, newden)
-
- def __eq__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum == secondnum
-
- def __gt__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum > secondnum
-
- def __lt__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum < secondnum
-
- def __ge__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum >= secondnum
-
- def __le__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum <= secondnum
-
- def __ne__(self, otherfraction):
- firstnum = self.num * otherfraction.den
- secondnum = otherfraction.num * self.den
- return firstnum != secondnum
-
- def get_num(self):
- return self.num
-
- def get_den(self):
- return self.den
-
- def __radd__(self, otherfraction):
- return self
-
-
-# 约分函数:将分数约分为最简分数
-def reduce_fraction(fraction):
- """
- 将分数约分为最简分数
-
- :param fraction: 分数,Fraction 类型
- :return: 约分后的分数,Fraction 类型
- """
- # 求最大公约数
- def gcd(a, b):
- if b == 0:
- return a
- else:
- return gcd(b, a % b)
-
- g = gcd(fraction.get_num(), fraction.get_den())
- return Fraction(fraction.get_num() // g, fraction.get_den() // g)
-
-
-# 通分函数:将多个分数通分
-def common_denominator(fractions):
- """
- 将多个分数通分
-
- :param fractions: 分数列表,Fraction 类型
- :return: 通分后的分数列表,Fraction 类型
- """
- # 求最大公约数
- def gcd(a, b):
- if b == 0:
- return a
- else:
- return gcd(b, a % b)
-
- # 求最小公倍数
- def lcm(a, b):
- return a * b // gcd(a, b)
-
- # 通分
- denominator = 1
- for fraction in fractions:
- denominator = lcm(denominator, fraction.get_den())
- for i in range(len(fractions)):
- fractions[i] = Fraction(fractions[i].get_num() * denominator // fractions[i].get_den(), denominator)
- return fractions
diff --git a/main.py b/main.py
index 54b4a2f..1e43525 100644
--- a/main.py
+++ b/main.py
@@ -4,14 +4,17 @@
# 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。
import parser
-from validity_check import IllegalAtomException
+import equation_solver
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
- eq = input('请输入化学方程式:')
- try:
- print(parser.parse_equation(eq))
- except IllegalAtomException as e:
- print(e.args[0])
+ while True:
+ eq = input('请输入化学方程式:')
+ try:
+ eq = parser.parse_equation(eq)
+ equation_solver.solve_equation(eq)
+ print('化学方程式:', parser.format_equation(eq))
+ except Exception as e:
+ print(e.args[0])
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
diff --git a/parser.py b/parser.py
index dda3f7d..a2c2df5 100644
--- a/parser.py
+++ b/parser.py
@@ -156,3 +156,27 @@ def parse_equation(eq):
'left': left,
'right': right
}
+
+
+def format_molecule(molecule):
+ """
+ 化学式的字典表示转化为字符串
+ :param molecule: 化学式的字典表示
+ :return: None
+ """
+ if molecule['coefficient'] != 1:
+ return str(molecule['coefficient']) + molecule['pretty_name']
+ else:
+ return molecule['pretty_name']
+
+def format_equation(eq):
+ """
+ 将化学方程式的字典表示转化为字符串,需要包含系数
+ :param eq: 化学方程式的字典表示
+ :return: 化学方程式的字符串表示
+ """
+ left = eq['left']
+ right = eq['right']
+ left = [format_molecule(molecule) for molecule in left]
+ right = [format_molecule(molecule) for molecule in right]
+ return ' + '.join(left) + ' => ' + ' + '.join(right)
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..adfd4e9
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+numpy~=1.23.5
+scipy~=1.9.3
\ No newline at end of file