史上最最最没用程序——自写平衡化学方程式
創(chuàng)作歷史
- 前言
- 例子
- 運行截圖
- bce-console.exe運行結果(pip install bce安裝)
- 總程序運行結果
- 需要的庫
- 步驟
- 拆分等式左右側
- 拆分每個化學物質和+-號
- 化學物質組成
- 化學物質提取
- 統(tǒng)計每個化學物質內每個化學元素的數(shù)量
- 判斷等式雙方元素種類是否一致
- 計算左右雙方元素數(shù)量,獲取每個元素在每個物質中的占比
- 解化學元素守恒方程
- 按物質名稱創(chuàng)建符號變量
- 根據(jù)左右元素總量關系創(chuàng)建關系式
- 求解各個化學物質之間的比例關系
- 取比例組最小公倍數(shù)為化學物質系數(shù)
- 總代碼
- 結束語
前言
在幾天前無意中看到了有這么一個 bce 庫,能夠平衡化學方程式系數(shù),我就想著這不就是配配元素嘛,自己按看到的例子進行嘗試,結果…
程序設計上越來越復雜,也不知道他那 bce-console.exe 僅僅 104KB 大小的程序怎么就能如此迅速配平化學方程(可能比較懶,所以像后面電子守恒和未知數(shù)關系的我就沒有去研究如何做對應的解決方案)。
例子
>> 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 # 正則表達式庫,用于提取元素和數(shù)目 import string # 獲取所有空白字符,替換掉化學方程式中多余空白字符以防出現(xiàn)意外 from sympy import solve, symbols # 用于解方程的庫,solve是求解函數(shù),symbols可以生成符號變量步驟
這里采用等式左右兩邊所有化學元素數(shù)量種類守恒的方式來解決配平問題。
拆分等式左右側
由于本文不考慮其他守恒規(guī)則,所以先過濾掉其他規(guī)則的化學方程式(最后只剩下例子的前4個)。
這里的 left 和 right 就是化學方程式等號左右兩邊的內容。
拆分每個化學物質和±號
化學物質組成
欲獲得化學物質,首先得知道有哪些化學元素符號。
當然這邊的化學物質是包括電子和未知數(shù)倍數(shù)關系提取的,所以很長。
這結果并不是完全只有化學物質,還包括了化學物質后的 ± 號,雖然沒有電子參與一般都是 + 號。
化學物質提取
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)到這里所有化學物質和運算符 ± 就全部被提取出來了,如何用一行代碼讓他們從二層列表中脫出來到一層列表呢?用萬能的匿名表達式就能實現(xiàn)
get_all = lambda _, __ = eval("[]"): __.clear() or [[__.append(____) for ____ in ___ if ____] for ___ in _] and __這里為什么要用 __ = eval("[]") 而不用 __ = [] 或 __ = list() 呢?這里先簡單的解釋一下 python 是怎么幫我們把值賦給變量的:
python會把同一值所在地址賦值給變量,也就是說,兩個不同變量名賦值同一值,他們的內存地址相同【id(XXX)相同】。所以這邊如果賦值為空列表,那會干擾其他同時運行的空列表,所以使用空列表作為默認參數(shù)會有問題,解釋器會報凸顯的黃色,而使用 eval("[]") 計算出的列表就不會報黃警告,雖然這也是空列表,不過為了避免多次執(zhí)行出現(xiàn)的變量意外,我在使用前執(zhí)行了 list..clear() 來清空列表,這樣最后得到的結果只會有后面執(zhí)行的。然后 or 后面的列表并不是需要的結果,只是借助了列表施展列表推導式,在推導式中為列表變量添加值,最后用 and 變量將提取出元素的列表值傳回去。
統(tǒng)計每個化學物質內每個化學元素的數(shù)量
判斷等式雙方元素種類是否一致
這里的 ELEMENT 是用來提取出所有化學元素包括電子的。
這里為什么不直接用 assert elements == right_elements 呢?因為列表是有序的,只要順序不一樣列表就不一樣,這里只需要種類一致,所以采用集合相等,或集合反交集為空來判斷兩邊元素種類是否一致。
計算左右雙方元素數(shù)量,獲取每個元素在每個物質中的占比
接下來就是統(tǒng)計左右元素數(shù)量,scale_group 用于獲取每個元素在不同物質中的數(shù)量,left_item_dict 和 right_item_dict 用于獲取每個物質中每個元素的數(shù)量,先以物質鍵中獲取元素數(shù)量,再反轉以元素為鍵,獲取元素在不同物質中的數(shù)量。
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]] = 0item_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]解化學元素守恒方程
按物質名稱創(chuàng)建符號變量
symbols_list = symbols([item for item in (left_item_list + right_item_list) if item not in list('+-')], positive=True, integer=True, real=True) # 為每個化學物質創(chuàng)建元素符號 for symbol in symbols_list:globals()[str(symbol)] = symbol # 將符號變量釋放到全局,為下文eval()字符串轉變量算式做基礎根據(jù)左右元素總量關系創(chuàng)建關系式
關系為:每個元素中,左邊元素數(shù)量之和 - 右邊元素數(shù)量之和 = 0。
eval(temp_str) 是為了將字符串計算成符號變量方程
求解各個化學物質之間的比例關系
res 為各個化學物質系數(shù)之間比例關系的解:{其他化學物質:n * 某化學物質}。
can_zhao 為作為參照的化學物質,通過每個比例除以它得到純數(shù)字比例,并將它設為1,就得到了他們之間的數(shù)字比例關系。
觀察到一般 slove 求解的關系比例分子已經(jīng)是最小公倍數(shù),而且分母只會有一個數(shù),所以此處將分母提取出來讓所有比例數(shù)字乘以分母,得到最小公倍數(shù)系數(shù)比例組。
取比例組最小公倍數(shù)為化學物質系數(shù)
生成配平的化學方程式,chemical_equations 就是需要的配平后的化學方程式
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 bceimport re import string from sympy import solve, symbols""" 邏輯流程: 1、拆分等式左右側 2、拆分每個化學物質和+-號 3、統(tǒng)計每個化學物質內每個化學元素的數(shù)量 4、解化學元素守恒方程 5、取比例組最小公倍數(shù)為化學物質系數(shù) 元素特征: 1、倍數(shù)元素:\d|{[A-Za-z0-9+-]} 2、電荷元素:<(?:\d|{[A-Za-z0-9+-]})?e[+-]> 3、化學元素:[A-Z][a-z]?\.? 4、物質狀態(tài):\([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): # 兩個數(shù)最大公約數(shù)while num2:temp = num1 % num2num1, num2 = num2, tempreturn num1def min_gbs(num1, num2): # 兩個數(shù)最小公倍數(shù)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 element2def 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) # 為每個化學物質創(chuàng)建元素符號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]] = 0item_dict[item][element[0]] += element[1].isdigit() and int(element[1]) or 1for 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_zhaores[can_zhao] = 1fen_mu = re.compile(R'/(\d+)?').findall(str(res))if fen_mu:bs = int(fen_mu[0])for item in res:res[item] *= bschemical_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_equationsfor 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))))結束語
當然了,我這個只能解決沒有電子轉移和沒有未知數(shù)關系的方程式,剩余的比較懶沒想做,有興趣的可以完善我這個程序哦~
想要標準的化學方程式配平結果,請安裝 bce 庫,使用 bce-console.exe 程序求解配平。
幾天后可能就要手術去了,屆時我將不能及時回復各個評論。作為第一個也是最后一個不正緊的打算,就像這樣隨意嘗試一下,最后得出結論:程序員還是搬運代碼來的香~ (前人的知識就是現(xiàn)人的工具)
總結
以上是生活随笔為你收集整理的史上最最最没用程序——自写平衡化学方程式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 后端传输图片Base64数据到前端渲染
- 下一篇: 计算机考研英语一和英语二的区别,考研英语