I saw this question on Quora:
A teacher assigns each of her 18 students a different integer from 1 through 18. The teacher forms pairs of study partners by using the rule that the sum of the pair of numbers is a perfect square. Assuming the 9 pairs of students follow this rule, the student assigned which number must be paired with the student assigned the number 1?
A. 16
B. 15
C. 9
D. 8
E. 3
I liked the approach that used a graph to illustrate and arrive at the answer. Below is the same approach using R.
# use the combn() function to generate all pair combinations (not permutations) m <- t(combn(1:18, 2)) head(m) [,1] [,2] [1,] 1 2 [2,] 1 3 [3,] 1 4 [4,] 1 5 [5,] 1 6 [6,] 1 7 tail(m) [,1] [,2] [148,] 15 16 [149,] 15 17 [150,] 15 18 [151,] 16 17 [152,] 16 18 [153,] 17 18
We can check which pairs are perfect squares by using sqrt() and modulus.
# integers have no remainders when divided by one 34 %% 1 [1] 0 # floats will have a remainder 34.1 %% 1 [1] 0.1 # numbers that are not perfect squares will # be a float when the square root is calculated sqrt(25) [1] 5 sqrt(26) [1] 5.09902
Now to put it all together.
m <- t(combn(1:18, 2)) # store rows that sum to a perfect square v <- vector() # counter k <- 0 # go through each row for (i in 1:nrow(m)){ # sum the pairs s <- m[i,1] + m[i,2] # check perfect square if (sqrt(s)%%1 == 0){ k <- k + 1 v[k] = i } } # subset perfect pairs into p p <- m[v,] p [,1] [,2] [1,] 1 3 [2,] 1 8 [3,] 1 15 [4,] 2 7 [5,] 2 14 [6,] 3 6 [7,] 3 13 [8,] 4 5 [9,] 4 12 [10,] 5 11 [11,] 6 10 [12,] 7 9 [13,] 7 18 [14,] 8 17 [15,] 9 16 [16,] 10 15 [17,] 11 14 [18,] 12 13
Finally, creating the graph.
# install if necessary #install.packages('igraph') library(igraph) net <- graph.data.frame(p, directed = FALSE) plot(net, layout = layout_components(net))
Since 16, 17, and 18 must pair with 9, 8, and 7, respectively, 2 must pair with 14, 11 with 5, 4 with 12, 13 with 3, 6 with 10. Therefore 1 has to pair with 15.
This work is licensed under a Creative Commons
Attribution 4.0 International License.
Nice, solution. But why do you use this annoying loop?
A vectorized approach looks IMO much better: `m[sqrt(rowSums(m)) %% 1 == 0, ]` even more as R is optimized for such operations.
As usual, I agree with you! Subsetting using logicals is much better. I just feel the approach I went with can be understood by a wider audience, especially those not familiar with R. But thank you for pointing out that it’s not the most efficient way. I should write a disclaimer.