生肉-待完善

This commit is contained in:
_Karasu_ 2022-12-10 21:38:17 +08:00
parent 12df1e8d57
commit 9eee646100
7 changed files with 136 additions and 128 deletions

View File

@ -10,5 +10,6 @@
<component name="PyDocumentationSettings"> <component name="PyDocumentationSettings">
<option name="format" value="PLAIN" /> <option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" /> <option name="myDocStringFormat" value="Plain" />
<option name="renderExternalDocumentation" value="true" />
</component> </component>
</module> </module>

7
.idea/other.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PySciProjectComponent">
<option name="PY_SCI_VIEW" value="true" />
<option name="PY_SCI_VIEW_SUGGESTED" value="true" />
</component>
</project>

View File

@ -1,3 +1,9 @@
from fractions import Fraction
import numpy as np
import scipy as sp
def solve_equation(eq): def solve_equation(eq):
""" """
配平化学方程式返回配平后的化学方程式 配平化学方程式返回配平后的化学方程式
@ -15,12 +21,94 @@ def solve_equation(eq):
:return: 配平后的化学方程式与输入格式相同 :return: 配平后的化学方程式与输入格式相同
若无法配平则返回 None 若无法配平则返回 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

View File

@ -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

15
main.py
View File

@ -4,14 +4,17 @@
# 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。 # 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。
import parser import parser
from validity_check import IllegalAtomException import equation_solver
# 按间距中的绿色按钮以运行脚本。 # 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__': if __name__ == '__main__':
eq = input('请输入化学方程式:') while True:
try: eq = input('请输入化学方程式:')
print(parser.parse_equation(eq)) try:
except IllegalAtomException as e: eq = parser.parse_equation(eq)
print(e.args[0]) 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 帮助 # 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助

View File

@ -156,3 +156,27 @@ def parse_equation(eq):
'left': left, 'left': left,
'right': right '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)

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
numpy~=1.23.5
scipy~=1.9.3