Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
The Problem
You have a 2×2 grid of squares, and you need to paint each square one of four colors: red, blue, green, or yellow. The restriction is that no two adjacent squares (sharing a side) can have the same color. How many valid ways you can color the grid?
This problem is a simplified illustration of the four color theorem, which says that four colors are sufficient to color any planar map such that no two adjacent regions share the same color. Additionally, it can be viewed as a specific case of a more generalized problem: coloring a
The Solution
There are many ways to solve this. In each solution, we count the number of combinations that are “correct” or valid, i.e., adhere to the rules given above in the problem. For the solution in R, we will generate all possible combinations of coloring this grid with no restrictions, and using that, directly count cases that follow the adjacency rules specified in the problem.
I will use dplyr
to illustrate how easy and straightforward it will be to get to the solution.
The simplest way to calculate ways to color this grid with no restrictions is to consider that for each of the
We now use R to generate these
library(gtools) x <- c('r', 'b', 'g', 'y') P <- permutations( n = 4, r = 4, v = x, repeats.allowed = T ) colnames(P) <- c("A", "B", "C", "D") P <- data.frame(P) nrow(P)
[1] 256
head(P)
A B C D 1 b b b b 2 b b b g 3 b b b r 4 b b b y 5 b b g b 6 b b g g
How do we now determine which cases do not follow the adjacency rules? To do this, we’ll need to use a pattern-searching logic to flag them. One way is to calculate, for all
However, in R, using dplyr
, we combine this in a single command using case when
, which will flag each case whenever it meets the invalidity condition even once, e.g., for the coloring scheme “blue-blue-blue-red”, it will flag it “invalid” since just the existence of “blue-blue” is sufficient to deem this scheme invalid.
library(dplyr) library(magrittr) case_split <- P %>% mutate(concat = paste(P$A, P$B, P$C, P$D, P$A, sep = "")) %>% mutate( flag = case_when( grepl("rr", concat) ~ "invalid", grepl("bb", concat) ~ "invalid", grepl("gg", concat) ~ "invalid", grepl("yy", concat) ~ "invalid", .default = "valid" ) ) %>% group_by(flag) %>% summarise(count = n()) knitr::kable(case_split, align = rep('c', 2))
flag | count |
---|---|
invalid | 172 |
valid | 84 |
There are
Of these remaining
flag_invalid_adj <- function(df) { df %>% rowwise() %>% mutate( # Check each adjacency pair for invalidity ab_invalid = A == B, bc_invalid = B == C, cd_invalid = C == D, ad_invalid = A == D, # Count the number of invalid pairs invalid = sum(ab_invalid, bc_invalid, cd_invalid, ad_invalid), ) %>% ungroup() } cols <- P[, 1:4] invalid_data <- flag_invalid_adj(cols) %>% select(A, B, C, D, invalid) finalCounts <- invalid_data %>% group_by(invalid) %>% summarise(count = n()) knitr::kable(finalCounts, align = rep('c', 2))
invalid | count |
---|---|
0 | 84 |
1 | 96 |
2 | 72 |
4 | 4 |
The first row confirms the count of ggplot2
, let’s generate 1 sample grid from each category of invalidity, so that we can visually inspect what we’ve been saying. The “4 invalid cases” has 4 pairs of blue, or 4 adjacencies, while the “No invalid cases!” plot is an example of how we can indeed color a graph, map, or a grid with 4 colors with no adjacent squares or “regions”, having the same color. Some notes on the code used – we plot the 4 examples using gridExtra
. We’ll also use textGrob
to attribute an overall title for the family of plots, using an elegant from the Google library (“rouge script”). Click “Show the code” to see the details.
# Select first row of all groups s <- invalid_data %>% group_by(invalid) %>% filter(row_number() == 1) # Load necessary libraries for elegant visualizations library(ggplot2) library(grid) # To ensure "textGrob" works library(gridExtra) library(showtext) # For using custom s # Load a using showtext _add_google("Rouge Script", "rouge script") # Example: Adding the "Lobster" showtext_auto() # Automatically use showtext for all plots # Create my color tibble (4x4) sq <- s %>% mutate_all( ~ case_when( . == "b" ~ "blue", . == "g" ~ "green", . == "y" ~ "yellow", . == "r" ~ "red", TRUE ~ . # Keep original value if it doesn't match )) # Convert tibble to a matrix color_matrix <- sq[, -5] %>% select(A, B, D, C) %>% as.matrix() # to match the coloring order of ggplot2 # Check the dimensions of the matrix # Print(dim(color_matrix)) # Should be 4x4 # Function to create a plot based on a color vector and a title create_plot <- function(colors, title) { df <- expand.grid(x = 0:1, y = 0:1) df$color <- colors ggplot(df, aes(x = x, y = y, fill = color)) + geom_tile(color = "white") + # Change border color to white scale_fill_identity() + # Use the colors as they are theme_minimal() + # Use a minimal theme coord_fixed() + # Keep aspect ratio labs(title = title) + # Add the title theme( legend.position = "none", # Remove legend axis.title = element_blank(), # Suppress axis titles axis.text = element_blank(), # Suppress axis text axis.ticks = element_blank(), # Suppress axis ticks panel.grid = element_blank(), # Suppress grid lines plot.title = element_text( family = "rouge script", size = 16, hjust = 0.5 ) # Use the custom for the title ) } # Create a vector of titles for each plot titles <- c("4 invalid cases", "2 invalid cases", "1 invalid case", "No invalid cases!") # Create a list of plots using the color matrix and titles plots <- lapply(1:nrow(color_matrix), function(i) { create_plot(color_matrix[i, ], titles[i]) }) # Create the overall title using textGrob overall_title <- textGrob( "Using R to elegantly illustrate the solution to a Combinatorics puzzle", gp = gpar(size = 20, family = "rouge script") ) # Create a spacer with appropriate height spacer <- rectGrob( gp = gpar(fill = NA, col = NA), width = unit(1, "npc"), height = unit(0.5, "lines") ) # Arrange the overall title and plots grid.arrange( overall_title, spacer, arrangeGrob(grobs = plots, ncol = 2), ncol = 1, heights = c(1, 0.1, 4) )
About the Author
Vidisha writes: “As a statistician and a data geek, I love solving puzzles of all kinds, with a particular fondness for combinatorial challenges. Recently, I tackled one in R using the dplyr package, which prompted me to think about the broader applications of R, not just as a powerful tool for solving puzzles for enthusiasts but also as an empowering way to teach high school mathematics. Moreover, R’s capabilities in data visualization can help learners grasp complex problems more intuitively, ultimately leading to a deeper understanding of their solutions.”
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.