Peter’s blog ✴ Week 387 ✴ 17 August 2026
THE WEEKLY CHALLENGE
Ones and atoms
You are given a chemical formula with elements, numbers, and parentheses. Write a script to count the total number of each type of atom by expanding all grouped multipliers. Then, format and return the final inventory as a single string sorted alphabetically by element name, including the total count only if it is greater than 1.
Example 1 Input: $formula = '((N2O)3(H2O)2)2' Output: 'H8N12O10' Step 1: Expand the innermost parentheses (N2O)3 => N = 2*3 = 6, O = 1*3 = 3 => N6O3 (H2O)2 => H = 2*2 = 4, O = 1*2 = 2 => H4O2 Step 2: Combine inside the outer parentheses Formula becomes: (N6O3 H4O2)2 Sum up identical elements inside: (N6 H4 O5)2 Step 3: Apply the outer multiplier N = 6*2 = 12 H = 4*2 = 8 O = 5*2 = 10 Step 4: Sort alphabetically and format Alphabetical order: H, N, O Counts: H: 8, N: 12, O: 10 Example 2 Input: $formula = 'Mg3(PO4)2' Output: 'Mg3O8P2' Step 1: Parse ungrouped elements Mg3 => Mg = 3 Step 2: Expand parentheses (PO4)2 P = 1*2 = 2 O = 4*2 = 8 Step 3: Total up counts Mg = 3 P = 2 O = 8 Step 4: Sort alphabetically and format Alphabetical order: Mg, O, P Counts: Mg: 3, O: 8, P: 2 Example 3 Input: $formula = '(((H)2)3)4' Output: 'H24' Step 1: Expand innermost level (H)2 H = 1*2 = 2 => formula becomes ((H2)3)4 Step 2: Expand middle level (H2)3 H = 2*3 = 6 => formula becomes (H6)4 Step 3: Expand outer level (H6)4 H = 6*4 = 24 Step 4: Sort alphabetically and format Single element: H: 24 Example 4 Input: $formula = 'NaCl3(O2(S10)2)2Mg' Output: 'Cl3MgNaO4S40' Step 1: Expand innermost parentheses (S10)2 S = 10*2 = 20 => inner formula becomes => O2S20 Step 2: Expand outer parentheses (O2S20)2 O = 2*2 = 4 S = 20*2 = 40 Step 3: Combine all parts Ungrouped start: Na (Na = 1), Cl3 (Cl = 3) Expanded middle: O = 4, S = 40 Ungrouped end: Mg (Mg = 1) Step 4: Sort alphabetically and format Alphabetical order: Cl (3), Mg (1), Na (1), O (4), S (40) Omit the number 1 for Mg and Na. Example 5 Input: $formula = 'Z2Y3(X2W)2' Output: 'W2X4Y3Z2' Step 1: Parse ungrouped elements Z2 => Z = 2 Y3 => Y = 3 Step 2: Expand parentheses (X2W)2 X = 2*2 = 4 W = 1*2 = 2 Step 3: Total up counts W = 2, X = 4, Y = 3, Z = 2 Step 4: Sort alphabetically and format Alphabetical order: W (2), X (4), Y (3), Z (2)
Some of these chemical formulae are rather strange, so pleased don't try synthesising them at home.
I approached this task by splitting the supplied string working backwards, ie right to left. A token comprises:
I work right to left so that when I encounter an element symbol, I have already parsed the number following it, and any numbers following the brackets which enclose the symbol.
I then increment $c{$x} by $n * $m
so that when I'm finished, for example,
$c{'He'} == 3.
And then sort keys %c gives the answer.
#!/usr/bin/perl
# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge/387/2
use v5.26; # The Weekly Challenge - 2026-08-17
use utf8; # Week 387 - task 2 - Atoms count
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';
use Encode;
atoms_count('((N2O)3(H2O)2)2');
atoms_count('Mg3(PO4)2');
atoms_count('(((H)2)3)4');
atoms_count('NaCl3(O2(S10)2)2Mg');
atoms_count('Z2Y3(X2W)2');
sub atoms_count {
my ($f, $m, $n, %c, @s, $k);
# initialise
$f = shift;
say qq[\nInput: '$f'];
$m = $n = 1;
%c = ();
# tokenise
$f = reverse($f);
while ($f) {
# digits + close bracket
if ($f =~ m|^(\d+)\)(.*)|) {
push @s, $1;
$m *= $1;
# digits
} elsif ($f =~ m|^(\d+)(.*)|) {
$n = reverse($1);
# symbol A or Ab
} elsif ($f =~ m|^([a-z]?[A-Z])(.*)|) {
$c{reverse($1)} += $n * $m;
$n = 1;
# open bracket
} elsif ($f =~ m|^(\()(.*)|) {
$m /= pop @s;
# close bracket
} elsif ($f =~ m|^(\))(.*)|) {
} else {
last;
}
$f = $2;
}
# report
print qq[Output: '];
for $k (sort keys %c) {
$c{$k} = '' if $c{$k} < 2;
print qq[$k$c{$k}];
}
say q['];
}
27 lines of code
Completed after the closing date and not submitted to GitHub
Input: '((N2O)3(H2O)2)2' Output: 'H8N12O10' Input: 'Mg3(PO4)2' Output: 'Mg3O8P2' Input: '(((H)2)3)4' Output: 'H24' Input: 'NaCl3(O2(S10)2)2Mg' Output: 'Cl3MgNaO4S40' Input: 'Z2Y3(X2W)2' Output: 'W2X4Y3Z2'
Any content of this website which has been created by Peter Campbell Smith is in the public domain