Given an atomic number in the inclusive range [1,118], write the shortest program/function that outputs one of {2,8,18} if the element with that atomic number follows the duplet, octet, or 18-electron rule, respectively.
Notes
- The Octet/Duplet/18-electron rule is a rule of thumb that describes how many electrons is needed for an element to completely fill their outer electron shells.
- The Duplet rule only applies to H, He, and Li (atomic numbers 1, 2, and 3)
- The 18-electron rule applies to all transition metals (21-30, 39-48, 72-80, 104-112), lanthanides (57-71), and actinides (89-103).
- The Octet rule applies to everything else (4-20, 31-38, 49-56, 81-88, 113-118).
Test Cases
input => output
1 => 2
2 => 2
3 => 2
4 => 8
18 => 8
19 => 8
20 => 8
21 => 18
26 => 18
30 => 18
31 => 8
92 => 18
118 => 8
-
\$\begingroup\$ Similar: Electron Configurations, Electron Configuration \$\endgroup\$bigyihsuan– bigyihsuan2023年02月10日 16:26:57 +00:00Commented Feb 10, 2023 at 16:26
-
\$\begingroup\$ I think there's a typo - it says the Octet rule applies to "133-118", it should be "113-118" \$\endgroup\$The Thonnu– The Thonnu2023年02月10日 17:39:38 +00:00Commented Feb 10, 2023 at 17:39
-
1\$\begingroup\$ @TheThonnu fixed \$\endgroup\$bigyihsuan– bigyihsuan2023年02月10日 17:40:39 +00:00Commented Feb 10, 2023 at 17:40
-
\$\begingroup\$ As a former chemistry major, I'd like to point out that the "octet" elements are those of the last 6 columns of the periodic table, wrapping round to the first 2 columns, to form runs of 8 elements. Element 4 Beryllium would then fit as a duplet, but in reality Be2+ in water borrows back 8 electrons from H2O to form Be(4*H2O)2+ to become like neon, so Wikipedia calls it an additional octet element. As for the other elements being 18-electron: the actual situation with lanthanides and actinides is way more complex but changing the rules of the question at this stage will render answers invalid \$\endgroup\$Level River St– Level River St2023年02月10日 21:28:16 +00:00Commented Feb 10, 2023 at 21:28
11 Answers 11
Excel, 60 bytes
=IFNA(8+10*ISEVEN(MATCH(A1,{4,21,31,39,49,57,81,89,113})),2)
Input in cell A1.
Charcoal, 22 bytes
I⊗§"|↖5Z·0χ⦄P↑mMρbl∨"N
Try it online! Link is to verbose version of code. Explanation: Most of the program is simply a compressed look-up table.
"..." Compressed look-up table
§ Indexed by
N Input as a number
⊗ Doubled
I Cast to string
Implicitly print
Vyxal, (削除) 19 (削除ここまで) 18 bytes
»μ7!3⁄4∷»4τ¦>ḣ∑∷+›2d
Try it Online! or verify all test cases.
-1 byte by porting part of Jonathan Allan's Jelly answer.
»...» # push compressed integer
4τ # convert to base 26
¦ # cumulative sum
this results in the list [3,20,30,38,48,56,80,88,112]
> # is less than input?
ḣ # extract head, pushes a[0] and a[1:] (a[0] == 0 iff input is less than 4)
∑ # sum
∷ # modulo 2 (this is 1 iff the 18-electron rule applies)
+ # add
› # increment
2 # square
d # double
Ruby, 42 bytes
->m{m>3?8+5*(2&70352637804063<<-m/2+12):2}
Unfortunately this fairly unimaginative approach works best.
Ruby rounds toward -infinity, so the minus sign in -m/2 ensures that transitions occur in the right place. This expression evaluates to -10 for both 19 and 20 input, and -11 for both 21 and 22 input. To get the first 18 output, we take this -11 and add 12 for an overall shift of +1. This gives a final answer of 8+5*(2) which is 18. Larger numbers give negative leftshift, which Ruby interprets as positive rightshift.
the magic number can be replaced by 0x3ffc3ffc3e1f
which is the same number of bytes. If three leading zeroes are added to the hex number, +12 can be deleted, but it's the same number of bytes.
Ruby, 65 bytes
->m{n=0
" $(1:JZ".bytes{|i|n+=15<<i}
m>3?18-10*(1&n>>(m+67)/2):2}
-
\$\begingroup\$ Your 54-byte solution is actually 44 since it contains 10 trailing spaces... \$\endgroup\$dingledooper– dingledooper2023年02月11日 01:51:42 +00:00Commented Feb 11, 2023 at 1:51
-
\$\begingroup\$ @dingledooper thanks. It's 42 bytes now. \$\endgroup\$Level River St– Level River St2023年02月11日 02:22:01 +00:00Commented Feb 11, 2023 at 2:22
Jelly, 19 bytes
>"Þœ&08PXp"¤‘§ḂS‘2Ḥ
A monadic Link that accepts an integer from \$[1,118]\$ and yields the rule, \2ドル\$, \8ドル\$, or \18ドル\$.
How?
>"..."¤‘§ḂS‘2Ḥ - Link: Atomic number, A e.g. 48
"..."¤‘ - lists of code-page indices -> [[20,30,38,48,56,80,88,112],[3]]
> - (A) greater than? (that) [[ 1, 1, 1, 0, 0, 0, 0, 0],[1]]
§ - sums [3,1]
Ḃ - mod two [1,1]
S - sum 2
‘ - increment 3
2 - square 9
Ḥ - double 18
Python, (削除) 72 71 56 (削除ここまで) 55 bytes
lambda n:(n<4or sum(i>n for i in b"\x04\x15\x1f'19QYq")%2*5+4)*2
(But the \x04\x15\x1f should be replaced with the actual unprintable characters)
Based off Jos Woolley's Excel answer.
-15 thanks to dingledooper
-1 thanks to pan
Python, 66 bytes
lambda n:(n<4)*2+((20<n<31)+(38<n<49)+(56<n<81)+(88<n<113))*18or 8
-
\$\begingroup\$ In the second answer, you can save a bunch by using a byte string instead of an array. \$\endgroup\$dingledooper– dingledooper2023年02月10日 20:00:58 +00:00Commented Feb 10, 2023 at 20:00
-
\$\begingroup\$ @dingledooper thanks! I didn't think of that. \$\endgroup\$The Thonnu– The Thonnu2023年02月10日 20:11:59 +00:00Commented Feb 10, 2023 at 20:11
-
05AB1E, 20 bytes
49Þ1š•N‚Ÿи––•2вÅΓ·Iè
Port of @Neil's Charcoal answer.
Try it online or verify all test cases.
A port of @AndrovT's Vyxal answer is 20 bytes as well:
•¶sWü„•2вηO›ćsOÉ6«*Ì
Try it online or verify all test cases.
Make sure to upvote both @Neil's and @AndrovT's answers as well!
Explanation:
49 # Push 49
Þ # Cycle it indefinitely: [4,9,4,9,4,9,...]
1š # Prepend a 1: [1,4,9,4,9,4,9,...]
•N‚Ÿи––• # Push compressed integer 25350987139094
2в # Convert it to base-26 as list: [4,17,10,8,10,8,24,8,24,6]
ÅΓ # Run-length decode the two lists: [1,1,1,1,4,4,4,...]
· # Double each: [2,2,2,2,8,8,8,...]
Iè # (0-based) index the input into it
# (after which the result is output implicitly)
•¶sWü„• # Push compressed integer 766210902312
2в # Convert it to base-26 as list: [3,17,10,8,10,8,24,8,24]
η # Get all prefixed of this list: [[3],[3,17],[3,17,10],...]
O # Sum each inner prefix: [3,20,30,38,48,56,80,88,112]
› # Check if the (implicit) input-integer is larger than the values
ć # Extract head; push first item and remainder-list separately
s # Swap so the remainder-list is at the top of the stack
O # Sum it
É # Modulo-2 (with an is_odd check)
6« # Append a 6 to this 0 or 1
* # Multiply it to the extracted head
Ì # Increase it by 2
# (after which the result is output implicitly)
See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •N‚Ÿи––• is 25350987139094; •N‚Ÿи––•2в is [4,17,10,8,10,8,24,8,24,6]; •¶sWü„• is 766210902312; and •¶sWü„•2в is [3,17,10,8,10,8,24,8,24].
JavaScript (ES6), 42 bytes
A hash formula with a 32-bit lookup.
n=>n<4?2:60620415>>(-~n>>1)*223%511&1?18:8
JavaScript (ES6), 47 bytes
A longer but slightly more subtle method.
n=>n<4?2:--n%16>="_468080"[n>>4]^n>29&n<32?18:8
Method
This is based on the pattern that appears when the atomic numbers are arranged by groups of \16ドル\$, starting at \0ドル\$ instead of \1ドル\$. We use the following condition to determine whether it's 18-electon or octet:
$$(n \bmod 16)\ge T_{\lfloor n/16 \rfloor}$$
There's unfortunately an exception for \$n=30\$ and \$n=31\$ (atomic numbers \31ドル\$ and \32ドル\$) which is not golfed very efficiently, making this method overall less competitive than the one using a more basic hash.
In the diagram below, we use +, . and x for duplet, octet and 18-electron respectively.
| 0 1 2 3 4 5 6 7 8 9 A B C D E F
---+--------------------------------
00 | + + + . . . . . . . . . . . . .
10 | . . . . x x x x x x x x x x . .
20 | . . . . . . x x x x x x x x x x
30 | . . . . . . . . x x x x x x x x
40 | x x x x x x x x x x x x x x x x
50 | . . . . . . . . x x x x x x x x
60 | x x x x x x x x x x x x x x x x
70 | . . . . . .
C (GCC), (削除) 81 (削除ここまで) (削除) 76 (削除ここまで) (削除) 71 (削除ここまで) (削除) 68 (削除ここまで) 57 bytes
thanks neil for -11 bytes
f(x){x=x<4?2:0x1fffffe01fffffe01ff807fell<<20>>x&1?18:8;}
Return 2 for <= 3, otherwise lookup the value in the 64-bit constants, where 0 is the 18 rule and 1 is the 8 rule.
-
\$\begingroup\$ Save 11 bytes by using a 128-bit integer constant:
f(x){x=x<4?2:0x1fffffe01fffffe01ff807fell<<20>>x&1?18:8;}\$\endgroup\$Neil– Neil2023年02月10日 22:55:18 +00:00Commented Feb 10, 2023 at 22:55
Pip, (削除) 61 47 (削除ここまで) 41 bytes
Ia<4P2EL{P%#({\a>a}FI(A*"&08PXp"))*t+8}
-20 thanks to @DLosc
Explanation
Ia<4 # if the input is less than 4:
P2 # print 2
EL{ # else:
P # print the following:
%#( # length mod 2 of:
{\a>a} # is the input greater than this number?
FI(A*"...") # filter the list [20,30,38,48,56,80,88,112] by the above
)*t+8 # times 10 plus 8
} # enf if
Old:
Ia<4 # if the input is less than 4:
P2 # print 2
EL{ # else:
Fi { # for i in ...
A*"..." # the ASCII code points of the string
# [20,30,38,48,56,80,88,112]:
l:lAE # append the following to l
(a>i) # is the input greater than i?
} # end for
P # print ...
(1Nl) # ... the count of 1s in l ...
% # ... mod 2 ...
*t+8 # ... times 10 plus 8
} # end if
-
\$\begingroup\$ Probably the shortest way to generate the list
[20;30;38;48;56;80;88;112], if you don't mind a couple of unprintable characters, is to make an 8-character string with those codepoints and useA*to get the ASCII code of each character: ATO.l:lAE(a>i)can belAE:a>i, but in this case I'd suggest instead filtering the list and taking the length of the result. Also, several of the curly braces and parentheses are unnecessary; try removing them one pair at a time and see if you get the same results. \$\endgroup\$DLosc– DLosc2023年02月17日 23:06:10 +00:00Commented Feb 17, 2023 at 23:06 -
\$\begingroup\$ Oh, and
tis initialized to10, so that'll save a byte. \$\endgroup\$DLosc– DLosc2023年02月17日 23:08:03 +00:00Commented Feb 17, 2023 at 23:08
Scala, 103 bytes
Modified from @The Thonnu's answer.
Golfed version. Try it online!
def f(n:Int)=if(n<4)2 else(Seq[Byte](0x04,0x15,0x1f,0x27,0x31,0x39,0x51,0x59,0x71).count(_ >n)%2*5+4)*2
Ungolfed version.
object PythonToScala {
def f(n: Int): Int = {
val b = Array[Byte](0x04, 0x15, 0x1f, 0x27, 0x31, 0x39, 0x51, 0x59, 0x71)
if (n < 4) 2
else (b.count(_ > n) % 2 * 5 + 4) * 2
}
def main(args: Array[String]): Unit = {
for (i <- 1 to 118) {
println(s"$i \t-->\t ${f(i)}")
}
}
}