Build a Mass Spectrometer!
Mathematica, 108 bytes
Print@@Join@@({Characters@"CHON",#}ᵀ/.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 Null
s).
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",#}ᵀ
puts each such combination in a form like {{"C", 0}, {"H", 1}, {"O", 2}, {"N", 3}}
. (ᵀ
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.
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)