화학 방정식의 균형을 잡으십시오!


30

Bernd는 화학에 문제가있는 고등학생입니다. 클래스에서 그는 헵탄의 연소와 같이 그들이하고있는 실험에 대한 화학 방정식을 설계해야한다 :

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

수학은 정확하게 Bernd의 가장 강한 주제가 아니기 때문에 종종 반응의 생산과 교육 사이의 정확한 비율을 찾는 데 어려움을 겪고 있습니다. 당신은 Bernd의 교사이기 때문에 그를 도와주는 것이 당신의 일입니다! 유효한 화학 방정식을 얻는 데 필요한 각 물질의 양을 계산하는 프로그램을 작성하십시오.

입력

입력은 양이없는 화학 방정식입니다. 순수한 ASCII로 이것을 가능하게하기 위해 구독을 일반 숫자로 씁니다. 요소 이름은 항상 대문자로 시작하며 그 뒤에 마이너스가 올 수 있습니다. 분자는 +부호 로 분리되며 ASCII 아트 화살표 ->가 방정식의 양쪽에 삽입됩니다.

Al+Fe2O4->Fe+Al2O3

입력은 개행으로 끝나며 공백을 포함하지 않습니다. 입력이 유효하지 않으면 프로그램이 원하는대로 수행 할 수 있습니다.

입력이 1024자를 넘지 않는다고 가정 할 수 있습니다. 프로그램은 표준 입력, 첫 번째 인수 또는 런타임에 구현 정의 된 방식으로 입력을 읽을 수 있습니다.

산출

프로그램의 출력은 추가 숫자로 보강 된 입력 방정식입니다. 각 요소의 원자 수는 화살표의 양쪽에서 동일해야합니다. 위의 예에서 유효한 출력은 다음과 같습니다.

2Al+Fe2O3->2Fe+Al2O3

분자의 수가 1 인 경우, 떨어 뜨립니다. 숫자는 항상 양의 정수 여야합니다. 프로그램은 합계가 최소가되도록 숫자를 산출해야합니다. 예를 들어, 다음은 불법입니다 :

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

해결책이 없으면 인쇄

Nope!

대신에. 솔루션이없는 샘플 입력은

Pb->Au

규칙

  • 이것은 코드 골프입니다. 가장 짧은 코드가 승리합니다.
  • 귀하의 프로그램은 모든 합리적인 입력에 대해 적절한 시간 내에 종료되어야합니다.

테스트 사례

각 테스트 케이스에는 입력과 올바른 출력의 두 줄이 있습니다.

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

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

Pb->Au
Nope!

1
나는 틀릴 수 있지만 이것은 코드 골프보다는 프로그래밍 도전의 자연스러운 후보처럼 보인다.
DavidC

1
한 번은 내장 solve(함수를 사용 eval(하여 입력을 해석하기 위해 TI-89 그래프 계산기에 화학 방정식 솔버를 작성했습니다. :)
mellamokb

3
@mellamokb 왜 게시하지 않습니까? 독창성을 위해 저에게
ratchet freak

5
"당신은 Bernds 가정교사이므로 그를 도와주는 것이 당신의 일입니다!" P : - 나는 교사는 그가하지 않도록 오히려 그를 위해 쓰기 소프트웨어보다, 자신을 위해 생각하는 베른트을 가르치는한다고 생각했을 것이다
naught101

1
@KuilinLi 그것은 다른 것이 아닙니다.
FUZxxl

답변:


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

매스 매 티카 507

나는에 설명 된 증강 화학 조성 매트릭스 접근법을 사용했습니다.

LRThorne, 화학 반응 방정식의 균형을 맞추기위한 혁신적인 접근법 : 단순화 된 행렬-행렬 널 공간을 결정하는 역 기법. Chem.Educator , 2010, 15, 304-308 .

약간의 조정이 추가되었습니다. 모든 공간에서 정수 값을 보장하기 위해 null 공간 벡터의 조옮김을 요소의 최대 공약수로 나눕니다. 내 구현은 방정식 균형을 맞추기위한 솔루션이 둘 이상인 경우를 아직 처리하지 않습니다.

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"]

여기에 이미지 설명을 입력하십시오

분석

원소에 의한 화학 종으로 구성된 다음 화학 조성표를 설정하여 추가 널 (NULL) 벡터가 추가됩니다 (증강 된 화학 조성 표가 됨).

화학 성분 표

내부 세포는 매트릭스로서 제거되고 뒤집혀서 산출된다.

전도

가장 오른쪽 열이 추출되어 다음을 생성합니다.

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

벡터의 각 요소는 요소의 gcd (1/8)로 나뉘어 다음을 제공합니다.

{-1, -11, 7, 8}

화살표의 왼쪽에 음수 값이 배치됩니다. 이것의 절대 값은 원래 방정식의 균형을 맞추는 데 필요한 숫자입니다.

해결책


느낌표를 추가하는 것을 잊지 마십시오!
ardnew

:} 좋아, 나는 문자 수를 올렸다
DavidC

왼쪽 열이 아니라 오른쪽 열을 의미한다고 생각합니다. 설명 (+1)에 감사하지만 궁금합니다. 분자 수가 원소 수보다 하나 더 많은 경우가 아니라면 어떻게 패딩합니까? 지금 신문을 읽으려면 꺼짐.
피터 테일러

어떤 이유로 든, 나는 오늘 당신의 의견을 만났습니다. 예, "오른쪽 열"을 의미했습니다. 패딩이 사용되는 곳을 볼 수 없거나 기억할 수없는 작업을 수행 한 이후 많은 시간이 지났습니다. 죄송합니다.
DavidC

3

파이썬, 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

파이썬 2, 635 바이트

이전 바이트 수 : 794, 776, 774, 765, 759, 747, 735, 734, 720, 683, 658, 655, 654, 653, 651, 638, 637, 636 바이트.

두 번째 들여 쓰기 수준은 탭일 뿐이고, 세 번째 들여 쓰기 수준은 공백입니다.

솔직히, 이것은 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!'

세 바이트를 저장 : ''.join(map(chr,range(97,122))): D
aliqandil

:( 작동하지 않습니다. 그러나 map(chr,range(97,123))12 바이트를 절약 할 수 있습니다.
Zacharý

아 맞다! 파이썬 2입니다!
aliqandil

1

자바 스크립트, 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

자바 스크립트, 705 바이트

(경쟁이 아닌 일부 기능은 도전 과제를 게시합니다)

다른 솔루션에는 모두 무차별 강제 요소가있었습니다. 나는 화학 방정식을 일련의 선형 방정식으로 표현한 다음 Gauss-Jordan 알고리즘을 사용하여 행렬의 행-에 첼론을 줄임으로써 더 결정적인 접근 방식을 시도했습니다. 모든 것이 0 인 사소한 경우를 분리하기 위해 요소 중 하나가 상수 수라고 가정하고 그 수는 분수를 갖지 않기 위해 모든 수를 곱한 것으로 결정됩니다. 마지막 단계로 우리는 마지막 조건을 만족시키기 위해 각각을 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
일부 기능은 도전 과제를 게시하기 때문에 비경쟁.
Zacharý

와우,이게 몇살인지 몰랐어요. 감사!
Kuilin Li
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.