Find the Oxidation States

7

1

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.

Beta Decay

Posted 2017-05-22T10:19:24.767

Reputation: 21 478

For H2O, can we output +1, +1, -2? Also, are we allowed to output without the + for positive integers? – Okx – 2017-05-22T10:25:27.143

@Okx Yes, you can output +1, +1, -2 and yes, you don't need the positive sign – Beta Decay – 2017-05-22T10:26:27.457

Can we accept input with an multiplier after every Atom: H2O1 or C1O2 – Roman Gräf – 2017-05-22T10:59:35.767

@RomanGräf Nope, you must take input as it is shown – Beta Decay – 2017-05-22T11:11:19.613

Shouldn't the output of CH4 be -4, +1, +1, +1, +1? – sudee – 2017-05-22T12:08:22.993

1(okay this might be cheating) Are we allowed to assume there will always be less than 10 of the same atom in a molecule, as my solution breaks with double digits it? :P – Okx – 2017-05-22T12:28:55.843

The total oxidation state should always add up to zero. H2O2 adds up to -2. – sudee – 2017-05-22T12:36:52.077

@JonathanAllan Final sum should always be zero. – sudee – 2017-05-22T12:45:39.937

Argh, so much confusing wording. It says in one place that H "always" has constant oxidation state +1. Then it says that that in H2, all of the atoms "always" have state 0. And "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." is really confusing - I assume what you mean is, there may be one single atom in the molecule which is not defined somewhere, so you need to deduce what its state is, given the other rules. – Steve Bennett – 2017-05-22T13:30:43.320

@SteveBennett Yes, you're right. I've cleared that up a bit – Beta Decay – 2017-05-22T13:32:13.770

@BetaDecay Could you answer my reply further up? – Okx – 2017-05-22T15:39:52.120

@Okx Oh sorry, no, you must support any number of atoms – Beta Decay – 2017-05-22T15:41:59.750

Can you add a testcase with a number >9 ? – ovs – 2017-05-22T17:30:59.333

Answers

1

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)}}

Try it online!

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==1 becomes a[1] with an inverted consequence. This is because we know that the string is well formed and so a.size != 0
  • ($2=='' ? 1:$2.to_i) becomes [1,$2.to_i].max

Both of these save 5 bytes.

Jenkar

Posted 2017-05-22T10:19:24.767

Reputation: 211

The max solution is pretty nice, didn't occur to me. I was hesitant about a[1] but then I dismissed it for some reason. :D We could also replace .inject:+ with .sum for newer versions of Ruby to save a few bytes. (Doesn't work with TIO sadly.) – sudee – 2017-05-22T18:25:14.080

2

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)}}

Try it online!

  • 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
    }
}

sudee

Posted 2017-05-22T10:19:24.767

Reputation: 551

Jenkar suggests saving one byte with [?O] instead of %w[O]. – Martin Ender – 2017-05-22T13:43:48.107

1

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

Try it online!

ovs

Posted 2017-05-22T10:19:24.767

Reputation: 21 408

Doesn't work with more than one digit in a number. – Okx – 2017-05-22T15:40:45.220

@Okx fixed it . – ovs – 2017-05-22T17:34:58.697