化学式のバランスをとる!


30

ベルントは化学にいくつかの問題がある高校生です。クラスでは、ヘプタンの燃焼など、彼らが行っているいくつかの実験のために化学方程式を設計する必要があります。

C 7 H 16 + 11O 2 →7CO 2 + 8H 2 O

数学は正確にベルントの最強の主題ではないので、彼はしばしば反応のプロダクトとエダクトの正確な比率を見つけるのに苦労しています。あなたはベルントの家庭教師なので、彼を助けるのはあなたの仕事です!有効な化学式を得るために必要な各物質の量を計算するプログラムを作成します。

入力

入力は、量のない化学式です。これを純粋なASCIIで実現するために、サブスクリプションを通常の番号として記述します。要素名は常に大文字で始まり、その後に極小が続く場合があります。分子は+記号で区切られ、ASCIIアートの矢印->が方程式の両側に挿入されます。

Al+Fe2O4->Fe+Al2O3

入力は改行で終了し、スペースは含まれません。入力が無効な場合、プログラムは何でも好きなように実行できます。

入力が1024文字より長くなることはないと想定できます。プログラムは、標準入力、最初の引数、またはどちらも不可能な場合は実行時に定義された実装から入力を読み取ることができます。

出力

プログラムの出力は、追加の数値で増補された入力方程式です。各要素の原子の数は、矢印の両側で同じでなければなりません。上記の例の場合、有効な出力は次のとおりです。

2Al+Fe2O3->2Fe+Al2O3

分子の数が1の場合、ドロップします。数値は常に正の整数でなければなりません。プログラムは、合計が最小になるような数値を生成する必要があります。たとえば、次は違法です。

40Al+20Fe2O3->40Fe+20Al2O3

解決策がない場合は、印刷してください

Nope!

代わりに。解のないサンプル入力は

Pb->Au

ルール

  • これはコードゴルフです。最短のコードが優先されます。
  • プログラムは、すべての妥当な入力に対して妥当な時間内に終了する必要があります。

テストケース

各テストケースには、入力と正しい出力の2行があります。

C7H16+O2->CO2+H2O
C7H16+11O2->7CO2+8H2O

Al+Fe2O3->Fe+Al2O3
2Al+Fe2O3->2Fe+Al2O3

Pb->Au
Nope!

1
私は間違っている可能性がありますが、これはゴルフをコーディングするよりもプログラミングの挑戦の自然な候補のようです。
-DavidC

1
TI-89グラフ計算機で、組み込みsolve(関数を使用eval(し、入力を解釈するために化学方程式ソルバーを書いたことがあります:)
mellamokb

3
@mellamokbあなたはそれを投稿していないなぜ、あなたは独創性のために私からupvoteを取得します
ラチェットフリーク

5
「あなたはベルンズの家庭教師なので、彼を助けるのはあなたの仕事です!」P: -私は家庭教師は彼が持っていないので、むしろ、彼の書き込みソフトウェアよりも、自分自身のために考えることベルントを教えるべきであると思っているだろう
naught101

1
@KuilinLiそれは間違っていない、ただ違う。
FUZxxl 16

回答:


7

C、442 505文字

// element use table, then once parsed reused as molecule weights
u,t[99];

// molecules
char*s,*m[99]; // name and following separator
c,v[99][99]; // count-1, element vector

i,j,n;

// brute force solver, n==0 upon solution - assume at most 30 of each molecule
b(k){
    if(k<0)for(n=j=0;!n&&j<u;j++)for(i=0;i<=c;i++)n+=t[i]*v[i][j]; // check if sums to zero
    else for(t[k]=0;n&&t[k]++<30;)b(k-1); // loop through all combos of weights
}

main(int r,char**a){
    // parse
    for(s=m[0]=a[1];*s;){
        // parse separator, advance next molecule
        if(*s==45)r=0,s++;
        if(*s<65)m[++c]=++s;
        // parse element
        j=*s++;
        if(*s>96)j=*s+++j<<8;            
        // lookup element index
        for(i=0,t[u]=j;t[i]-j;i++);
        u+=i==u;
        // parse amount
        for(n=0;*s>>4==3;)n=n*10+*s++-48;
        n+=!n;
        // store element count in molecule vector, flip sign for other side of '->'
        v[c][i]=r?n:-n;
    }
    // solve
    b(c);
    // output
    for(i=0,s=n?"Nope!":a[1];*s;putchar(*s++))s==m[i]&&t[i++]>1?printf("%d",t[i-1]):0;
    putchar(10);
}

として実行:

./a.out "C7H16+O2->CO2+H2O"
./a.out "Al+Fe2O4->Fe+Al2O3"
./a.out "Pb->Au"

結果:

C7H16+11O2->7CO2+8H2O
8Al+3Fe2O4->6Fe+4Al2O3
Nope!

+1これはプレよりもはるかに立派です。ディベート
-ardnew

2
中括弧を避けるために、ステートメント区切り文字としてコンマを使用してみてください。また、if-then-else-constructsを三項演算子に置き換えて、コードを短くしてみてください。t [i]> 1?printf( "%s"、t [i]):0; 1バイト短くなります。また、m [0]は* mと同じです。
FUZxxl

6

Mathematica 507

私は、で説明されている化学組成マトリックスの強化アプローチを採用しました

LRThorne、化学反応方程式のバランスをとる革新的なアプローチ:単純化されたマトリックス-マトリックスのヌル空間を決定するための逆手法。Chem.Educator2010、15、304-308

微調整が1つ追加されました。ソリューションの整数値を保証するために、要素の最大公約数でヌル空間ベクトルの転置を除算しました。私の実装では、方程式のバランスをとるソリューションが複数ある場合はまだ処理しません。

b@t_ :=Quiet@Check[Module[{s = StringSplit[t, "+" | "->"], g = StringCases, k = Length, 
  e, v, f, z, r},
e = Union@Flatten[g[#, _?UpperCaseQ ~~ ___?LowerCaseQ] & /@ s];v = k@e;
s_~f~e_ := If[g[s, e] == {}, 0, If[(r = g[s, e ~~ p__?DigitQ :> p]) == {}, 1, 
   r /. {{x_} :> ToExpression@x}]];z = k@s - v;
r = #/(GCD @@ #) &[Inverse[Join[SparseArray[{{i_, j_} :> f[s[[j]], e[[i]]]}, k /@ {e, s}], 
Table[Join[ConstantArray[0, {z, v}][[i]], #[[i]]], {i, k[#]}]]][[All, -1]] &
   [IdentityMatrix@z]];
Row@Flatten[ReplacePart[Riffle[Partition[Riffle[Abs@r, s], 2], " + "], 
   2 Count[r, _?Negative] -> " -> "]]], "Nope!"]

テスト

b["C7H16+O2->CO2+H2O"]
b["Al+Fe2O3->Fe+Al2O3"]
b["Pb->Au"]

ここに画像の説明を入力してください

分析

これは、要素ごとの化学種で構成される次の化学組成表を設定することにより機能し、それに追加の無効性ベクトルが追加されます(拡張化学組成表になります:

化学成分表

内側のセルはマトリックスとして削除され、反転され、降伏します。

反転

右端の列が抽出され、次の結果が得られます。

{-(1/8)、-(11/8)、7 / 8、1}

ベクトルの各要素は、要素のgcd(1/8)で除算され、次のようになります。

{-1、-11、7、8}

負の値は矢印の左側に配置されます。これらの絶対値は、元の方程式のバランスを取るために必要な数値です。

溶液


感嘆符を追加することを忘れないでください!
ardnew

:} OK、文字数を増やしました
-DavidC

左側の列ではなく、右側の列を意味すると思います。説明(+1)には感謝していますが、分子の数が要素の数よりも1つ多い場合ではない場合、どのようにパッドしますか?今すぐ論文を読むためにオフ。
ピーターテイラー

何らかの理由で、今日あなたのコメントに出くわしました。はい、「右側の列」という意味でした。私がこれに取り組んでから非常に多くの時間が経過したため、パディングが使用されている場所がわかりません(または覚えていません)。ごめんなさい。
DavidC

3

Python、880文字

import sys,re
from sympy.solvers import solve
from sympy import Symbol
from fractions import gcd
from collections import defaultdict

Ls=list('abcdefghijklmnopqrstuvwxyz')
eq=sys.argv[1]
Ss,Os,Es,a,i=defaultdict(list),Ls[:],[],1,1
for p in eq.split('->'):
 for k in p.split('+'):
  c = [Ls.pop(0), 1]
  for e,m in re.findall('([A-Z][a-z]?)([0-9]*)',k):
   m=1 if m=='' else int(m)
   a*=m
   d=[c[0],c[1]*m*i]
   Ss[e][:0],Es[:0]=[d],[[e,d]]
 i=-1
Ys=dict((s,eval('Symbol("'+s+'")')) for s in Os if s not in Ls)
Qs=[eval('+'.join('%d*%s'%(c[1],c[0]) for c in Ss[s]),{},Ys) for s in Ss]+[Ys['a']-a]
k=solve(Qs,*Ys)
if k:
 N=[k[Ys[s]] for s in sorted(Ys)]
 g=N[0]
 for a1, a2 in zip(N[0::2],N[1::2]):g=gcd(g,a2)
 N=[i/g for i in N]
 pM=lambda c: str(c) if c!=1 else ''
 print '->'.join('+'.join(pM(N.pop(0))+str(t) for t in p.split('+')) for p in eq.split('->'))
else:print 'Nope!'

テスト:

python chem-min.py "C7H16+O2->CO2+H2O"
python chem-min.py "Al+Fe2O4->Fe+Al2O3"
python chem-min.py "Pb->Au"

出力:

C7H16+11O2->7CO2+8H2O
8Al+3Fe2O4->6Fe+4Al2O3
Nope!

880に満たないかもしれませんが、私の目はすでに私を殺しています...


2

Python 2、635バイト

以前のバイトカウント:794、776、774、765、759、747、735、734、720、683、658、655、654、653、651、638、637、636バイト。

2番目のインデントレベルはタブのみで、3番目はタブ、次にスペースです。

正直なところ、これはjadkik94の答えですが、非常に多くのバイトが削られたので、私はそれをしなければなりませんでした。バイトを削ることができるかどうか教えてください!

from sympy import*
import sys,re
from sympy.solvers import*
from collections import*
P=str.split
L=map(chr,range(97,123))
q=sys.argv[1]
S,O,a,i,u,v=defaultdict(list),L[:],1,1,'+','->'
w=u.join
for p in P(q,v):
 for k in P(p,u):
     c=L.pop(0)
     for e,m in re.findall('([A-Z][a-z]*)(\d*)',k):
      m=int(m or 1)
      a*=m
      S[e][:0]=[c,m*i],
 i=-1
Y=dict((s,Symbol(s))for s in set(O)-set(L))
Q=[eval(w('%d*%s'%(c[1],c[0])for c in S[s]),{},Y)for s in S]+[Y['a']-a]
k=solve(Q,*Y)
if k:
 N=[k[Y[s]]for s in sorted(Y)]
 g=gcd(N[:1]+N[1::2])
 print v.join(w((lambda c:str(c)*(c!=1))(N.pop(0)/g)+str(t)for t in P(p,u))for p in P(q,v))
else:print'Nope!'

3バイトを保存します::''.join(map(chr,range(97,122)))D-
アリカンジル

:(、それは機能しません。ただし、map(chr,range(97,123))保存された12バイトで機能します
。-ザカリー

そうそう!それはpython 2です!
アリカンディル

1

JavaScript、682バイト

x=>{m=1;x.split(/\D+/g).map(i=>i?m*=i:0);e=new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g));e.delete``;A=[];for(let z of e){t=x.split`->`;u=[];for(c=1;Q=t.shift();c=-1)Q.split`+`.map(p=>u.push(c*((i=p.indexOf(z))==-1?0:(N=p.substring(i+z.length).match(/^\d+/g))?N[0]:1)));A.push(u)}J=A.length;for(P=0;P<J;P++){for(i=P;!A[i][P];i++);W=A.splice(i,1)[0];W=W.map(t=>t*m/W[P]);A=A.map(r=>r[P]?r.map((t,j)=>t-W[j]*r[P]/m):r);A.splice(P,0,W)}f=e.size;if(!A[0][f])return"Nope!";g=m=-m;_=(a,b)=>b?_(b,a%b):a;c=[];A.map(p=>c.push(t=p.pop())&(g=_(g,t)));c.push(m);j=x.match(/[^+>]+/g);return c.map(k=>k/g).map(t=>(t^1?t:"")+(z=j.shift())+(z.endsWith`-`?">":"+")).join``.slice(0,-1);}

これは、Kuilinの答えのはるかに多くの(キャラクターの数十!)特定のJS機能が課題をポストデートするため、競合しない可能性があります。


0

Javascript、705バイト

(非競合、一部の機能は課題の後期です)

他のソリューションにはすべて、総当たり攻撃の要素がありました。化学方程式を一連の線形方程式として表現し、Gauss-Jordanアルゴリズムを使用して行列の簡約行エシュロン形式を解くことにより、より決定的なアプローチを試みました。すべてがゼロであるという些細なケースを分離するために、要素の1つが定数であると仮定します。最後のステップとして、最後の条件を満たすためにそれぞれをgcdで除算します。

ゴルフをしていない:

function solve(x) {
	//firstly we find bigNumber, which will be all numbers multiplied together, in order to assume the last element is a constant amount of that
	bigNumber = 1;
	arrayOfNumbers = new Set(x.split(/\D+/g));
	arrayOfNumbers.delete("");
	for (let i of arrayOfNumbers) bigNumber *= parseInt(i);
	
	//first actual step, we split into left hand side and right hand side, and then into separate molecules
	//number of molecules is number of variables, number of elements is number of equations, variables refer to the coefficients of the chemical equation
	//note, the structure of this is changed a lot in the golfed version since right is the same as negative left
	left = x.split("->")[0].split("+");
	righ = x.split("->")[1].split("+");
	molecules = left.length + righ.length;
	
	//then let's find what elements there are - this will also become how many equations we have, or the columns of our matrix minus one
	//we replace all the non-element characters, and then split based on the uppercase characters
	//this also sometimes adds a "" to the array, we don't need that so we just delete it
	//turn into a set in order to remove repeats
	elems = new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g));
	elems.delete("");
	
	rrefArray = [];//first index is rows, second index columns - each row is an equation x*(A11)+y*(A21)+z*(A31)=A41 etc etc, to solve for xyz as coefficients
	//loop thru the elements, since for each element we'll have an equation, or a row in the array
	for (let elem of elems) {
		buildArr = [];
		//loop thru the sides
		for (let molecule of left) {
			//let's see how many of element elem are in molecule molecule
			//ASSUMPTION: each element happens only once per molecule (no shenanigans like CH3COOH)
			index = molecule.indexOf(elem);
			if (index == -1) buildArr.push(0);
			else {
				index += elem.length;
				numberAfterElement = molecule.substring(index).match(/^\d+/g);
				if (numberAfterElement == null) buildArr.push(1);
				else buildArr.push(parseInt(numberAfterElement));
			}
		}
		//same for right, except each item is negative
		for (let molecule of righ) {
			index = molecule.indexOf(elem);
			if (index == -1) buildArr.push(0);
			else {
				index += elem.length;
				numberAfterElement = molecule.substring(index).match(/^\d+/g);
				if (numberAfterElement == null) buildArr.push(-1);
				else buildArr.push(parseInt(numberAfterElement)*(-1));
			}
		}
		rrefArray.push(buildArr);
	}
	
	//Gauss-Jordan algorithm starts here, on rrefArray
	for (pivot=0;pivot<Math.min(molecules, elems.size);pivot++) {
		//for each pivot element, first we search for a row in which the pivot is nonzero
		//this is guaranteed to exist because there are no empty molecules
		for (i=pivot;i<rrefArray.length;i++) {
			row = rrefArray[i];
			if (row[pivot] != 0) {
				workingOnThisRow = rrefArray.splice(rrefArray.indexOf(row), 1)[0];
			}
		}
		//then multiply elements so the pivot element of workingOnThisRow is equal to bigNumber we determined above, this is all to keep everything in integer-space
		multiplyWhat = bigNumber / workingOnThisRow[pivot]
		for (i=0;i<workingOnThisRow.length;i++) workingOnThisRow[i] *= multiplyWhat
		//then we make sure the other rows don't have this column as a number, the other rows have to be zero, if not we can normalize to bigNumber and subtract
		for (let i in rrefArray) {
			row = rrefArray[i];
			if (row[pivot] != 0) {
				multiplyWhat = bigNumber / row[pivot]
				for (j=0;j<row.length;j++) {
					row[j] *= multiplyWhat;
					row[j] -= workingOnThisRow[j];
					row[j] /= multiplyWhat;
				}
				rrefArray[i]=row;
			}
		}
		//finally we put the row back
		rrefArray.splice(pivot, 0, workingOnThisRow);
	}
	
	//and finally we're done!
	//sanity check to make sure it succeeded, if not then the matrix is insolvable
	if (rrefArray[0][elems.size] == 0 || rrefArray[0][elems.size] == undefined) return "Nope!";
	
	//last step - get the results of the rref, which will be the coefficients of em except for the last one, which would be bigNumber (1 with typical implementation of the algorithm)
	bigNumber *= -1;
	gcd_calc = function(a, b) {
		if (!b) return a;
		return gcd_calc(b, a%b);
	};
	coEffs = [];
	gcd = bigNumber;
	for (i=0;i<rrefArray.length;i++) {
		num = rrefArray[i][molecules-1];
		coEffs.push(num);
		gcd = gcd_calc(gcd, num)
	}
	coEffs.push(bigNumber);
	for (i=0;i<coEffs.length;i++) coEffs[i] /= gcd;
	
	//now we make it human readable
	//we have left and right from before, let's not forget those!
	out = "";
	for (i=0;i<coEffs.length;i++) {
		coEff = coEffs[i];
		if (coEff != 1) out += coEff;
		out += left.shift();
		if (left.length == 0 && righ.length != 0) {
			out += "->";
			left = righ;
		} else if (i != coEffs.length-1) out += "+";
	}
	return out;
}
console.log(solve("Al+Fe2O4->Fe+Al2O3"));
console.log(solve("Al+Fe2O3->Fe+Al2O3"));
console.log(solve("C7H16+O2->CO2+H2O"));
console.log(solve("Pb->Au"));

ゴルフ

s=x=>{m=1;x.split(/\D+/g).map(i=>i!=""?m*=i:0);e=(new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g)));e.delete("");A=[];for(let z of e){t=x.split("->");u=[];for(c=1;Q=t.shift();c=-1)Q.split("+").map(p=>u.push(c*((i=p.indexOf(z))==-1?0:(N=p.substring(i+z.length).match(/^\d+/g))?N[0]:1)));A.push(u)}J=A.length;for(P=0;P<J;P++){for(i=P;!A[i][P];i++);W=A.splice(i,1)[0];W=W.map(t=>t*m/W[P]);A=A.map(r=>!r[P]?r:r.map((t,j)=>t-W[j]*r[P]/m));A.splice(P,0,W)}f=e.size;if (!A[0][f])return "Nope!";g=m=-m;_=(a,b)=>b?_(b,a%b):a;c=[];A.map(p=>c.push(t=p.pop())&(g=_(g,t)));c.push(m);j=x.match(/[^+>]+/g);return c.map(k=>k/g).map(t=>(t==1?"":t)+(z=j.shift())+(z.endsWith("-")?">":"+")).join("").slice(0,-1);}

console.log(s("Al+Fe2O4->Fe+Al2O3"));
console.log(s("Al+Fe2O3->Fe+Al2O3"));
console.log(s("C7H16+O2->CO2+H2O"));
console.log(s("Pb->Au"));


1
一部の機能はこの課題の後期であるため、競合しません。
ザカリー16

なんてこった、これが何歳なのか気づかなかった。ありがとう!
桂林李
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.