11
\$\begingroup\$

Challenge

Given the high resolution molecular mass of an organic molecule, output the molecule's molecular formula.

Explanation

The input will be a single number to three decimal places of precision, the relative molecular mass of the molecule.

Here, the molecular mass is defined as the sum of the masses of the atoms in the compound. Since you only are finding the molecular formulae of organic compounds, the atomic masses you need to know are:

  • C, Carbon: 12.011
  • H, Hydrogen: 1.008
  • O, Oxygen: 15.999
  • N, Nitrogen: 14.007

Your formula should only ever contain carbon, hydrogen, oxygen or nitrogen.

When writing the formula, it should take the form:

CaHbOcNd

Where the elements must be in that order (C -> H -> O -> N, so C2O8N4H6 should be C2H6O8N4) and a, b, c and d are numbers of the preceding element in the molecule (i.e. C2 means that there are two carbon atoms in the molecule).

If a, b, c or d are zero, that element should not be included in the formula (e.g. C2H6O2N0 should be C2H6O2). Finally, if a, b, c or d are one, you should not include the number in the formula (e.g. C1H4 should be CH4).

The input will always be valid (i.e. there will be a molecule with that mass). If the input is ambiguous (multiple molecules have the same mass), you must only output one of the molecules. How you choose this molecule is up to you.

Worked Example

Suppose the input is 180.156, there is only one combination of the elements which can have this molecular mass:

12.011*6 + 1.008*12 + 15.999*6 + 14.007*0 = 180.156

So there are:

  • 6 Carbons
  • 12 Hydrogens
  • 6 Oxygens
  • 0 Nitrogens

Therefore, your output should be:

C6H12O6

More Examples

Input -> Output
28.054 -> C2H4
74.079 -> C3H6O2
75.067 -> C2H5O2N
18.015 -> H2O

Winning

Shortest code in bytes wins.

asked May 3, 2017 at 16:44
\$\endgroup\$
8
  • 2
    \$\begingroup\$ What if the input is ambiguous? \$\endgroup\$ Commented May 3, 2017 at 16:46
  • \$\begingroup\$ @NoOneIsHere AFAIK the input should not be ambiguous, but I'll add that into the rules anyway. \$\endgroup\$ Commented May 3, 2017 at 16:49
  • \$\begingroup\$ Can input be taken as int (i.e., no period - ethene would be 28054) \$\endgroup\$ Commented May 3, 2017 at 16:52
  • 5
    \$\begingroup\$ 12.011 is the relative atomic mass of carbon, which is a weighted average of the relative isotopic masses of the isotopes. In a mass spectrometer, where different isotopes are distinguished, you should see exactly 12. Similar for other atoms. \$\endgroup\$ Commented May 3, 2017 at 17:04
  • 2
    \$\begingroup\$ For a fun test, note that the input 672.336 has 24 possible solutions, including a pure-nitrogen and a pure-hydrogen solution. \$\endgroup\$ Commented May 3, 2017 at 17:27

3 Answers 3

2
\$\begingroup\$

Mathematica, 108 bytes

Print@@Join@@({Characters@"CHON",#}T/.a_/;Last@a<2:>Table@@a)&/@{12011,1008,15999,14007}~FrobeniusSolve~#&

Pure function expecting the input as an integer (1000 times the relative molecular mass); it prints all possible answers to STOUD (and returns an array of Nulls).

The heavy lifting is done by the builtin {12011,1008,15999,14007}~FrobeniusSolve~#, which finds all nonnegative integer combinations of the hardcoded weights that equal the input. {Characters@"CHON",#}T puts each such combination in a form like {{"C", 0}, {"H", 1}, {"O", 2}, {"N", 3}}. (T is actually the 3-byte private Mathematica character U+F3C7.)

The transformation rule /.a_/;Last@a<2:>Table@@a changes pairs of the form {x, 0} to {} and pairs of the form {x, 1} to {x} (and spits out errors as it tries to apply to the whole expression as well). Then Print@@Join@@ prints the result in the correct form, avoiding the need to cast the integers as strings and concatenate.

answered May 3, 2017 at 17:38
\$\endgroup\$
3
  • \$\begingroup\$ What's the result of 672336? :) \$\endgroup\$ Commented May 3, 2017 at 17:45
  • \$\begingroup\$ The T seems to be the wrong character. Should be . \$\endgroup\$ Commented May 3, 2017 at 17:49
  • \$\begingroup\$ Yeah, have to make the choice between easy to read and easy to cut/paste. \$\endgroup\$ Commented May 4, 2017 at 1:28
2
\$\begingroup\$

Python 2, 242 bytes

b=[12011,1008,15999,14007]
def p(m):
 if m in b:x=[0,]*4;x[b.index(m)]=1;return x
 elif m<1:return 0
 else:
 for i in range(4):
 x=p(m-b[i])
 if x:x[i]+=1;return x
 return 0
print''.join(a+`n`*(n>1)for n,a in zip(p(input()),'CHON')if n)

Try it online!
Recursive function, the input is an integer (1000 times the relative molecular mass) thanks Stephen S for the idea


My machine took 40 segs to turn 672336 into C33H115O3N8 with this modified code. It contains a lookup table for hits/fails to reduce the amount of recursive calls and a optimization to count an element multiple times (if the mass is high enough)

answered May 3, 2017 at 17:50
\$\endgroup\$
3
  • \$\begingroup\$ Why does 180156 timeout when all of the test cases are so fast? (without the cache hit) \$\endgroup\$ Commented May 3, 2017 at 17:54
  • \$\begingroup\$ @BetaDecay hmm, could be 18015 instead? \$\endgroup\$ Commented May 3, 2017 at 18:02
  • \$\begingroup\$ Nope, 18015 is H2O, not C6H12O6 \$\endgroup\$ Commented May 3, 2017 at 18:30
2
\$\begingroup\$

JavaScript (ES6), (削除) 159 (削除ここまで) 158 bytes

Not exactly fast...

w=>[...Array(w**4|0)].some((_,n)=>![12011,1008,15999,14007].reduce((p,c,i)=>p-c*(x[i]=n%w|!(n/=w)),w*1e3,x=[]))&&x.map((v,i)=>('CHON'[i]+v).slice(0,v)).join``

Demo

let f =
w=>[...Array(w**4|0)].some((_,n)=>![12011,1008,15999,14007].reduce((p,c,i)=>p-c*(x[i]=n%w|!(n/=w)),w*1e3,x=[]))&&x.map((v,i)=>('CHON'[i]+v).slice(0,v)).join``
console.log(f(28.054)) // -> C2H4
console.log(f(18.015)) // -> H2O


Faster version, (削除) 174 (削除ここまで) 173 bytes

w=>[...Array(w**3|0)].some((_,n)=>r=(d=w*1e3-14007*(a=n/w/w%w|0)-15999*(b=n/w%w|0)-12011*(c=n%w|0))%1008|d<0?0:[c,d/1008,b,a])&&r.map((v,i)=>('CHON'[i]+v).slice(0,v)).join``

All test cases

let f=
w=>[...Array(w**3|0)].some((_,n)=>r=(d=w*1e3-14007*(a=n/w/w%w|0)-15999*(b=n/w%w|0)-12011*(c=n%w|0))%1008|d<0?0:[c,d/1008,b,a])&&r.map((v,i)=>('CHON'[i]+v).slice(0,v)).join``
console.log(f(180.156)) // -> C6H12O6
console.log(f(28.054)) // -> C2H4
console.log(f(74.079)) // -> C3H6O2
console.log(f(75.067)) // -> C2H5O2N
console.log(f(18.015)) // -> H2O

answered May 4, 2017 at 11:07
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.