0
点赞
收藏
分享

微信扫一扫

史上最最最没用程序——自写平衡化学方程式

J简文 2022-02-22 阅读 60
python

创作历史

前言

例子

>> P+O2=P2O5
4P+5O2=2P2O5
>> H2O(g)+Fe=Fe3O4+H2
4H2O(g)+3Fe=Fe3O4+4H2
>> CO+Fe2O3=Fe+CO2
3CO+Fe2O3=2Fe+3CO2
>> C2H5OH+O2=CO2+H2O
C2H5OH+3O2=2CO2+3H2O
>> Cl2+<e->=Cl<e->
Cl2+2<e->=2Cl<e->
>> Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>
Cu+2Fe<3e+>=Cu<2e+>+2Fe<2e+>
>> LiOH+H2O2+H2O=Li2O2.H2O2.3H2O
2LiOH+2H2O2+H2O=Li2O2.H2O2.3H2O
>> C{n}H{2n+2}+O2=CO2+H2O
{(n+1)^(-1)}C{n}H{2*n+2}+{(1/2)*(3*n+1)/(n+1)}O2={n/(n+1)}CO2+H2O
>> CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3
CH3(CHCH){n}CH3+{n}Cl2=CH3(CHClCHCl){n}CH3
>> X-<e->=X<{n}e+>
X-{n}<e->=X<{n}e+>
>> CH4;HCN;NH3;O2;H2O
2HCN+6H2O=2CH4+2NH3+3O2
>> CH4(g);HCN(g);NH3(g);O2(g);H2O(g)
2CH4(g)+2NH3(g)+3O2(g)=2HCN(g)+6H2O(g)

运行截图

bce-console.exe运行结果(pip install bce安装)

在这里插入图片描述

总程序运行结果

在这里插入图片描述

需要的库

import re  # 正则表达式库,用于提取元素和数目
import string  # 获取所有空白字符,替换掉化学方程式中多余空白字符以防出现意外
from sympy import solve, symbols  # 用于解方程的库,solve是求解函数,symbols可以生成符号变量

步骤

拆分等式左右侧

if '=' not in ChemicalEquations and ';' in ChemicalEquations:
    all_items = ChemicalEquations.split(';')
    pass  # 自动化合价计算等式先不做,先做元素配平
elif 'e+>' in ChemicalEquations or 'e->' in ChemicalEquations or '{' in ChemicalEquations or '.' in ChemicalEquations:
    pass  # 暂时不搞电子<e+>/<e->和含./{n}的化学物质等的运算
else:
    left, right = ChemicalEquations.split('=')

拆分每个化学物质和±号

化学物质组成

1、倍数元素:\d|{[A-Za-z0-9+-]}
2、电荷元素:<(?:\d|{[A-Za-z0-9+-]})?e[+-]>
3、化学元素:[A-Z][a-z]?\.?
4、物质状态:\([gls]\)  # g是气体,l是液体,s是固体
5、化学物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
6[]物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:[A-Za-z0-9.]+(?:\([A-Za-z0-9]+\))?(?:{[A-Za-z0-9+-]+})?(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)

化学物质提取

left, right = ChemicalEquations.split('=')
items = re.compile(R'[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)')
left_items, right_items = items.findall(left), items.findall(right)
get_all = lambda _, __ = eval("[]"): __.clear() or [[__.append(____) for ____ in ___ if ____] for ___ in _] and __

统计每个化学物质内每个化学元素的数量

判断等式双方元素种类是否一致

ELEMENT = re.compile(R'([A-Z][a-z]?\.?|<(?:\d|{[A-Za-z0-9+-]})?e[+-]>)')
elements = list(set(get_all([ELEMENT.findall(item) for item in left_item_list if item not in list('+-')])))
right_elements = list(set(get_all([ELEMENT.findall(item) for item in right_item_list if item not in list('+-')])))
# assert not (set(elements) ^ set(right_elements)), '化学方程式两方元素不守恒!'
assert set(elements) == set(right_elements), '化学方程式两方元素不守恒!'

计算左右双方元素数量,获取每个元素在每个物质中的占比

scale_group = {element: {} for element in elements}
left_item_dict = {item: {} for item in left_item_list if item not in list('+-')}
right_item_dict = {item: {} for item in right_item_list if item not in list('+-')}
for item_list, item_dict in ((left_item_list, left_item_dict), (right_item_list, right_item_dict)):
    for item in item_list:
        if item not in list('+-'):
            for element in re.compile(R'([A-Z][a-z]?)(\d*)').findall(item):
                if element[0] not in item_dict[item]:
                    item_dict[item][element[0]] = 0
                item_dict[item][element[0]] += element[1].isdigit() and int(element[1]) or 1
for element in elements:
    for item_dict in (left_item_dict, right_item_dict):
        for item in item_dict:
            if element in item:
                scale_group[element][item] = item_dict[item][element]

解化学元素守恒方程

按物质名称创建符号变量

symbols_list = symbols([item for item in (left_item_list + right_item_list) if item not in list('+-')], positive=True, integer=True, real=True)  # 为每个化学物质创建元素符号
for symbol in symbols_list:
    globals()[str(symbol)] = symbol  # 将符号变量释放到全局,为下文eval()字符串转变量算式做基础

根据左右元素总量关系创建关系式

solve_list = []
for element in elements:
    temp_str = ''
    temp = []
    for item in left_item_dict:
        index = scale_group[element].get(str(item), 
        if index:
            temp.append(f'{index}*{str(item)}')
    temp_str += '+'.join(temp)
    temp = []
    for item in right_item_dict:
        index = scale_group[element].get(str(item), 
        if index:
            temp.append(f'{index}*{str(item)}')
    if temp:
        temp_str += '-' + '-'.join(temp)
    solve_list.append(eval(temp_str))

求解各个化学物质之间的比例关系

res = solve(solve_list, symbols_list)
can_zhao = [item for item in symbols_list if str(item) == list(set(list(left_item_dict.keys()) + list(right_item_dict.keys())) - set(list(map(str, res.keys()))))[0]][0]
for item in res:
    res[item] /= can_zhao
res[can_zhao] = 1
fen_mu = re.compile(R'/(\d+)?').findall(str(res))
if fen_mu:
    bs = int(fen_mu[0])
    for item in res:
        res[item] *= bs

取比例组最小公倍数为化学物质系数

chemical_equations = []
for item_dict in (left_item_dict, right_item_dict):
    all_item = []
    all_index = []
    for item in item_dict:
        all_item.append(item)
        all_index.append(res[[i for i in symbols_list if str(i) == item][0]])
    chemical_equations.append(' + '.join([f'{"" if all_index[i] == 1 else all_index[i]}{all_item[i]}' for i in range(len(item_dict))]))
chemical_equations = ' = '.join(chemical_equations)

总代码

# _*_ coding:utf-8 _*_
# Project: 最最最没用程序合集
# FileName: AutoBalancingChemicalEquations.py
# UserName: user_from_future博主
# ComputerUser:user_from_future
# Day: 2022/2/21
# Time: 20:21
# IDE: PyCharm
# 2022年,所有bug都将会被丢到海里喂鲨鱼!我说的!不容反驳!

# 自动配平化学方程式
# import bce

import re
import string
from sympy import solve, symbols

"""
逻辑流程:
1、拆分等式左右侧
2、拆分每个化学物质和+-号
3、统计每个化学物质内每个化学元素的数量
4、解化学元素守恒方程
5、取比例组最小公倍数为化学物质系数
元素特征:
1、倍数元素:\d|{[A-Za-z0-9+-]}
2、电荷元素:<(?:\d|{[A-Za-z0-9+-]})?e[+-]>
3、化学元素:[A-Z][a-z]?\.?
4、物质状态:\([gls]\)  # g是气体,l是液体,s是固体
5、化学物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
6、[②]物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:[A-Za-z0-9.]+(?:\([A-Za-z0-9]+\))?(?:{[A-Za-z0-9+-]+})?(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)

试验过程:
>> P+O2=P2O5
4P+5O2=2P2O5
>> H2O(g)+Fe=Fe3O4+H2
4H2O(g)+3Fe=Fe3O4+4H2
>> CO+Fe2O3=Fe+CO2
3CO+Fe2O3=2Fe+3CO2
>> C2H5OH+O2=CO2+H2O
C2H5OH+3O2=2CO2+3H2O
>> Cl2+<e->=Cl<e->
Cl2+2<e->=2Cl<e->
>> Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>
Cu+2Fe<3e+>=Cu<2e+>+2Fe<2e+>
>> LiOH+H2O2+H2O=Li2O2.H2O2.3H2O
2LiOH+2H2O2+H2O=Li2O2.H2O2.3H2O
>> C{n}H{2n+2}+O2=CO2+H2O
{(n+1)^(-1)}C{n}H{2*n+2}+{(1/2)*(3*n+1)/(n+1)}O2={n/(n+1)}CO2+H2O
>> CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3
CH3(CHCH){n}CH3+{n}Cl2=CH3(CHClCHCl){n}CH3
>> X-<e->=X<{n}e+>
X-{n}<e->=X<{n}e+>
>> CH4;HCN;NH3;O2;H2O
2HCN+6H2O=2CH4+2NH3+3O2
>> CH4(g);HCN(g);NH3(g);O2(g);H2O(g)
2CH4(g)+2NH3(g)+3O2(g)=2HCN(g)+6H2O(g)
"""

ChemicalEquationsList = [
    'P+O2=P2O5',                                # 4P+5O2+2P2O5
    'H2O(g)+Fe=Fe3O4+H2',                       # 4H2O(g)+3Fe=Fe3O4+4H2
    'CO+Fe2O3=Fe+CO2',                          # 3CO+Fe2O3=2Fe+3CO2
    'C2H5OH+O2=CO2+H2O',                        # C2H5OH+3O2=2CO2+3H2O
    'Cl2+<e->=Cl<e->',                          # Cl2+2<e->=2Cl<e->
    'Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>',               # Cu+2Fe<3e+>=Cu<2e+>+2Fe<2e+>
    'LiOH+H2O2+H2O=Li2O2.H2O2.3H2O',            # 2LiOH+2H2O2+H2O=Li2O2.H2O2.3H2O
    'C{n}H{2n+2}+O2=CO2+H2O',                   # {(n+1)^(-1)}C{n}H{2*n+2}+{(1/2)*(3*n+1)/(n+1)}O2={n/(n+1)}CO2+H2O
    'CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3',  # CH3(CHCH){n}CH3+{n}Cl2=CH3(CHClCHCl){n}CH3
    'X-<e->=X<{n}e+>',                          # X-{n}<e->=X<{n}e+>
    'CH4;HCN;NH3;O2;H2O',                       # 2HCN+6H2O=2CH4+2NH3+3O2
    'CH4(g);HCN(g);NH3(g);O2(g);H2O(g)',        # 2CH4(g)+2NH3(g)+3O2(g)=2HCN(g)+6H2O(g)
]


ELEMENT = re.compile(R'([A-Z][a-z]?|<(?:\d|{[A-Za-z0-9+-]})?e[+-]>)')


def max_gys(num1, num2):  # 两个数最大公约数
    while num2:
        temp = num1 % num2
        num1, num2 = num2, temp
    return num1


def min_gbs(num1, num2):  # 两个数最小公倍数
    return num1 * num2 // max_gys(num1, num2)


# 将二层列表中的内容转成一层列表
get_all = lambda _, __ = eval("[]"): __.clear() or [[__.append(____) for ____ in ___ if ____] for ___ in _] and __


def handle_double_elements_list_scale(element1, element2, chemicals, scale_group):  # 同步两个元素组中相同的元素
    shared = list(set(chemicals[element1]) & set(chemicals[element2]))
    if shared:
        multiply = min_gbs(scale_group[element1][shared[0]], scale_group[element2][shared[0]])
        shared1 = scale_group[element1][shared[0]]
        for item in scale_group[element1]:
            scale_group[element1][item] *= (multiply // shared1)
        shared2 = scale_group[element2][shared[0]]
        for item in scale_group[element2]:
            scale_group[element2][item] *= (multiply // shared2)
    return element2


def calc_equations(left_item_list, right_item_list):
    elements = list(set(get_all([ELEMENT.findall(item) for item in left_item_list if item not in list('+-')])))
    right_elements = list(set(get_all([ELEMENT.findall(item) for item in right_item_list if item not in list('+-')])))
    assert set(elements) == set(right_elements), '化学方程式两方元素不守恒!'
    symbols_list = symbols([item for item in (left_item_list + right_item_list) if item not in list('+-')], positive=True, integer=True, real=True)  # 为每个化学物质创建元素符号
    for symbol in symbols_list:
        globals()[str(symbol)] = symbol  # 将符号变量释放到全局,为下文eval()字符串转变量算式做基础
    scale_group = {element: {} for element in elements}
    left_item_dict = {item: {} for item in left_item_list if item not in list('+-')}
    right_item_dict = {item: {} for item in right_item_list if item not in list('+-')}
    for item_list, item_dict in ((left_item_list, left_item_dict), (right_item_list, right_item_dict)):
        for item in item_list:
            if item not in list('+-'):
                for element in re.compile(R'([A-Z][a-z]?)(\d*)').findall(item):
                    if element[0] not in item_dict[item]:
                        item_dict[item][element[0]] = 0
                    item_dict[item][element[0]] += element[1].isdigit() and int(element[1]) or 1
    for element in elements:
        for item_dict in (left_item_dict, right_item_dict):
            for item in item_dict:
                if element in item:
                    scale_group[element][item] = item_dict[item][element]
    solve_list = []
    for element in elements:
        temp_str = ''
        temp = []
        for item in left_item_dict:
            index = scale_group[element].get(str(item), "")
            if index:
                temp.append(f'{index}*{str(item)}')
        temp_str += '+'.join(temp)
        temp = []
        for item in right_item_dict:
            index = scale_group[element].get(str(item), "")
            if index:
                temp.append(f'{index}*{str(item)}')
        if temp:
            temp_str += '-' + '-'.join(temp)
        solve_list.append(eval(temp_str))
    res = solve(solve_list, symbols_list)
    can_zhao = [item for item in symbols_list if str(item) == list(set(list(left_item_dict.keys()) + list(right_item_dict.keys())) - set(list(map(str, res.keys()))))[0]][0]
    for item in res:
        res[item] /= can_zhao
    res[can_zhao] = 1
    fen_mu = re.compile(R'/(\d+)?').findall(str(res))
    if fen_mu:
        bs = int(fen_mu[0])
        for item in res:
            res[item] *= bs
    chemical_equations = []
    for item_dict in (left_item_dict, right_item_dict):
        all_item = []
        all_index = []
        for item in item_dict:
            all_item.append(item)
            all_index.append(res[[i for i in symbols_list if str(i) == item][0]])
        chemical_equations.append(' + '.join([f'{"" if all_index[i] == 1 else all_index[i]}{all_item[i]}' for i in range(len(item_dict))]))
    chemical_equations = ' = '.join(chemical_equations)
    return chemical_equations


for ChemicalEquations in ChemicalEquationsList:
    for space in string.whitespace:
        ChemicalEquations = ChemicalEquations.replace(space, '')
    if '=' not in ChemicalEquations and ';' in ChemicalEquations:
        all_items = ChemicalEquations.split(';')
        pass  # 自动化合价计算等式先不做,先做元素配平
    elif 'e+>' in ChemicalEquations or 'e->' in ChemicalEquations or '{' in ChemicalEquations or '.' in ChemicalEquations:
        pass  # 暂时不搞电子<e+>/<e->和含./{n}的化学物质等的运算
    else:
        left, right = ChemicalEquations.split('=')
        items = re.compile(R'[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)')
        left_items, right_items = items.findall(left), items.findall(right)
        print(ChemicalEquations + '\n\t' + calc_equations(list(get_all(left_items)), list(get_all(right_items))))

结束语

举报

相关推荐

0 条评论