Challenge
Find the oxidation states of each of the atoms in a given molecule. These can be output as a list or otherwise.
Rules
The total oxidation state of a molecule will always be zero. The total oxidation state is the sum of the oxidation states of each of the individual atoms in the molecule.
The following atoms have constant oxidation states:
- Hydrogen,
H, always has state +1 - Oxygen,
O, always has state -2 - All Group 1 elements have a state +1
- All Group 2 elements have a state +2
- Fluorine,
F, always has state -1 - Chlorine,
Cl, always has state -1
In monatomic molecules (such as H2 or S8), all of the atoms always have a state 0. I.e. (H2 is 0, 0 and S8 is 0, 0, 0, 0, 0, 0, 0, 0)
The Group 1 elements are: Li, Na, K, Rb, Cs, Fr. The Group 2 elements are: Be, Mg, Ca, Sr, Ba, Ra.
You will be able to work out the states of every atom in the molecule given. There will be no ambiguous inputs (i.e. you won't be given H2O2 or P4Br6).
You should output the oxidation states of the individual atoms, not the total state.
If there is an element which is not listed in the list above in the molecule, you need to work out its oxidation state yourself since the sum of the oxidation states of all the atoms adds up to zero.
The molecules do not have to exist in real life.
You will never get a single atom such as Ti or F.
Built-in functions which access data about oxidation states are disallowed.
Examples
Input > Output
H2O > +1, +1, -2
CO2 > +4, -2, -2
CH4 > -4, +1, +1, +1, +1
H2SO4 > +1, +1, +6, -2, -2, -2, -2
NaHCO3 > +1, +1, +4, -2, -2, -2
XeF8 > +8, -1, -1, -1, -1, -1, -1, -1, -1
TiCl2 > +2, -1, -1
P4 > 0, 0, 0, 0
Winning
The shortest code in bytes wins.
3 Answers 3
Ruby, (削除) 288 (削除ここまで) 255 bytes
->m{h={[?O]=>-2,%w[F Cl]=>-1,%w[H Li Na K Rb Cs Fr]=>1,%w[Be Mg Ca Sr Ba Ra]=>2};a=m.scan /([A-Z][a-z]*\d*)/;r=a.flat_map{|(b)|b=~/(\D+)(\d*)/;[a.size==1?0:h[h.keys.find{|v|v.include?1ドル}]]*(2ドル=='' ? 1:2ドル.to_i)};r.map{|j|j||-((r-[p]).inject:+)/r.count(p)}}
- Saved 1 byte thanks to Jenkar
- Saved 32 more bytes by inlining definitions and removing parentheses
Ungolfed
oxidation_state = -> molecule {
h = {
%w[H] => 1,
%w[O] => -2,
%w[F] => -1,
%w[Cl] => -1,
%w[Li Na K Rb Cs Fr] => 1,
%w[Be Mg Ca Sr Ba Ra] => 2,
}
find_oxidation = -> a {
k = h.keys.find { |as| as.include?(a) }
h[k]
}
atoms = molecule.scan(/([A-Z][a-z]*\d*)/)
r = atoms.flat_map { |(a)|
a.match(/([^\d]+)(\d*)/)
[atoms.length == 1 ? 0 : find_oxidation[1ドル]] * (2ドル=='' ? 1 : 2ドル.to_i)
}
r.map { |j|
sum = r.compact.inject(:+)
!j ? -sum / r.count(nil) : j
}
}
-
\$\begingroup\$ Jenkar suggests saving one byte with
[?O]instead of%w[O]. \$\endgroup\$Martin Ender– Martin Ender2017年05月22日 13:43:48 +00:00Commented May 22, 2017 at 13:43
Ruby, 245 bytes
->m{h={[?O]=>-2,%w[F Cl]=>-1,%w[H Li Na K Rb Cs Fr]=>1,%w[Be Mg Ca Sr Ba Ra]=>2};a=m.scan /([A-Z][a-z]*\d*)/;r=a.flat_map{|(b)|b=~/(\D+)(\d*)/;[a[1]?h[h.keys.find{|v|v.include?1ドル}]:0]*[1,2ドル.to_i].max};r.map{|j|j||-((r-[p]).inject:+)/r.count(p)}}
As I prepare this post, with a ton of optimizations, sudee's answer gets an update xD
Which reduces my changes to two :
a.size==1becomesa[1]with an inverted consequence. This is because we know that the string is well formed and soa.size != 0(2ドル=='' ? 1:2ドル.to_i)becomes[1,2ドル.to_i].max
Both of these save 5 bytes.
-
\$\begingroup\$ The
maxsolution is pretty nice, didn't occur to me. I was hesitant abouta[1]but then I dismissed it for some reason. :D We could also replace.inject:+with.sumfor newer versions of Ruby to save a few bytes. (Doesn't work with TIO sadly.) \$\endgroup\$sudee– sudee2017年05月22日 18:25:14 +00:00Commented May 22, 2017 at 18:25
Python 3, 272 bytes
s=input()+'A';n='';l=[1]
for i in s:
if'Z'<i:l[-1]+=i
elif'9'<i:l+=[l.pop()]*int(n or 1)+[i];n=''
else:n+=i
i=lambda s:s.split().count
l=[i('Li H Na K Rb Cs Fr')(s)+i('Be Mg Ca Sr Ba Ra')(s)*2-i('F Cl O O')(s)for s in l[1:-1]]
print([k or-sum(l)//l.count(0)for k in l])
H2O, can we output+1, +1, -2? Also, are we allowed to output without the+for positive integers? \$\endgroup\$+1, +1, -2and yes, you don't need the positive sign \$\endgroup\$H2O1orC1O2\$\endgroup\$