How Many More Palindrome Dates Will There Be This Century?

Super Bowl Sunday, when the Chiefs came back from a 10 point deficit in the 4th quarter to defeat the 49ers 31-20, was also a palindrome day. Since we Westerners write the date in the format mm/dd/yyyy, we could have turned that around and had the same day as 02022020 is the same forward and backwards.

The Riddler Express asked how many more of these types of dates there will be this century. The Riddler was published on 02/07/2020. So, I first stored this as a date. R defaults to the format yyyy-mm-dd for dates. We’ll deal with that later.

RidDate <- as.Date("2020-02-07")

Next, I built a function that would take a date as an input, and return TRUE if the date is palindrome and FALSE if the date was not a palindrome. I called the function is.palindrome().

## The input x is a date 
is.palindrome <- function(x){
  P <- strsplit(format(x, "%m%d%Y"), split = "")[[1]]  
  ## with x as, say, 2043-05-19, P will now be a vector of characters that 
  ## will look like "0", "5", "1", "9", "2", "0", "4", "3"
  Pf <- P[1:4] ## Pf is the vector of characters "0", "5", "1", "9"
  Pl <- P[8:5] ## Pl is the reversed vector of characters "3", "4", "0", "2".  
  if(all.equal(Pf,Pl)!=TRUE){
    return(FALSE)
  }
  else{return(TRUE)}
}

## Test to see if it works
print("This one should return TRUE")
## [1] "This one should return TRUE"
is.palindrome(as.Date("2020-02-02"))
## [1] TRUE
print("This one should return FALSE")
## [1] "This one should return FALSE"
is.palindrome(RidDate)
## [1] FALSE

Now, we’ll use a while loop cycling through all of the dates until we get to 01/01/2100, the start of the new century. (Or would that be 01/01/2101 to be precise?)

PalindromeDates <- c()
loopDate <- RidDate + 1
while(loopDate < as.Date("2100-01-01")){
   PalindromeDates <- c(PalindromeDates, is.palindrome(loopDate))
   loopDate <- loopDate + 1
}

To find out the answer to our question, we simply find out how many TRUE’s are within my PalindromeDates vector.

sum(PalindromeDates)
## [1] 8

Well that wasn’t very satisfying. What are the actual dates in the format that we write them in?

format(RidDate + which(PalindromeDates), "%m/%d/%Y")
## [1] "12/02/2021" "03/02/2030" "04/02/2040" "05/02/2050" "06/02/2060"
## [6] "07/02/2070" "08/02/2080" "09/02/2090"

This is still not that satisyfing. If you’re not a computer, you probably don’t write a zero before the month. For fun, let’s check dates if we scratch the first 0. I’ll write a second function that does that.

is.palindrome2 <- function(x){ ## this will take a date
  P <- strsplit(format(x, "%m%d%Y"), split = "")[[1]]
  if(P[1]=="0"){
    P <- P[-1]
    Pf <- P[1:3]
    Pl <- P[7:5]
  }
  else{return(FALSE)}
  if(all.equal(Pf,Pl)!=TRUE){
    return(FALSE)
  }
  else{return(TRUE)}
}

Using a similar loop as above, let’s find the number of palindromes without a 0 to lead the month.

PalindromeDatesNo0 <- c()
loopDate <- RidDate + 1
while(loopDate < as.Date("2100-01-01")){
   PalindromeDatesNo0 <- c(PalindromeDatesNo0, is.palindrome2(loopDate))
   loopDate <- loopDate + 1
}

How many of these are there? And what dates are these?

sum(PalindromeDatesNo0)
## [1] 17
sapply(format(RidDate + which(PalindromeDatesNo0), "%m/%d/%Y"), function(x) substr(x, 2, 10))
##  01/20/2021  02/20/2022  03/20/2023  04/20/2024  05/20/2025  06/20/2026 
## "1/20/2021" "2/20/2022" "3/20/2023" "4/20/2024" "5/20/2025" "6/20/2026" 
##  07/20/2027  08/20/2028  09/20/2029  01/30/2031  03/30/2033  04/30/2034 
## "7/20/2027" "8/20/2028" "9/20/2029" "1/30/2031" "3/30/2033" "4/30/2034" 
##  05/30/2035  06/30/2036  07/30/2037  08/30/2038  09/30/2039 
## "5/30/2035" "6/30/2036" "7/30/2037" "8/30/2038" "9/30/2039"

This is much more satisfying.

What if we wrote our dates like the rest of the world did, in the format dd/mm/yyyy? Interestingly enough, Super Bowl Sunday would still have been a palindrome! Let’s find out how many more we get to add to the list.

is.palindrome3 <- function(x){
  P <- strsplit(format(x, "%d%m%Y"), split = "")[[1]]  
  ## with x as, say, 2043-05-19, P will now be a vector of characters that 
  ## will look like "1", "9", "0", "5", "2", "0", "4", "3"
  Pf <- P[1:4] ## Pf is the vector of characters "1", "9", "0", "5"
  Pl <- P[8:5] ## Pl is the reversed vector of characters "3", "4", "0", "2".  
  if(all.equal(Pf,Pl)!=TRUE){
    return(FALSE)
  }
  else{return(TRUE)}
}

PalindromeDatesWorld <- c()
loopDate <- RidDate + 1
while(loopDate < as.Date("2100-01-01")){
   PalindromeDatesWorld <- c(PalindromeDatesWorld, is.palindrome3(loopDate))
   loopDate <- loopDate + 1
}


print("The Total Number of Dates is ")
## [1] "The Total Number of Dates is "
sum(PalindromeDatesWorld)
## [1] 23
print("and those dates in the form dd/mm/yyyy are ")
## [1] "and those dates in the form dd/mm/yyyy are "
format(RidDate + which(PalindromeDatesWorld), "%d/%m/%Y")
##  [1] "12/02/2021" "22/02/2022" "03/02/2030" "13/02/2031" "23/02/2032"
##  [6] "04/02/2040" "14/02/2041" "24/02/2042" "05/02/2050" "15/02/2051"
## [11] "25/02/2052" "06/02/2060" "16/02/2061" "26/02/2062" "07/02/2070"
## [16] "17/02/2071" "27/02/2072" "08/02/2080" "18/02/2081" "28/02/2082"
## [21] "09/02/2090" "19/02/2091" "29/02/2092"

How many way to interpret the ambiguous |-1|-2|-3|-4|-5|-6|-7|-8|-9| ?

If we explore the ambiguous \(|-1|-2|-3|\) first. Let abs(x) represent |x|. Then \(|-1|-2|-3|\) can be interpreted as abs(-1)-2abs(-3), which is -5. OR, it can be interpreted as abs(-1abs(-2)-3), which is 5. Let’s build a function when there is just a vector of 3 values. This will take an input vector v = (-1,-2,-3) and a multiplier m (which will be used in recursive steps) with default value 1.

AbsValue3 <- function(v,m=1){
  if(length(v)!=3){return("Error")}
  else{
    return(c(m*abs(v[1])+v[2]*abs(v[3]), m*abs(v[1]*abs(v[2])+v[3])))
  }
}

## Does it work?
AbsValue3(-(1:3))
## [1] -5  5

Let’s extend this and find all of values of |-1|-2|-3|-4|-5|.

AbsValue5 <- function(v,m=1){
  if(length(v)!=5){return("Error")}
  else{
    a1 <- m*abs(v[1])+v[2]*abs(v[3])+v[4]*abs(v[5])
    a2 <- m*abs(v[1]*abs(v[2])+v[3])+v[4]*abs(v[5])
    a3 <- m*abs(v[1])+v[2]*abs(v[3]*abs(v[4])+v[5])
    a4 <- m*abs(AbsValue3(v[2:4],v[1])+v[5])
    return(c(a1,a2,a3,a4))
  }
}

## Check our work
AbsValue5(-(1:5))
## [1] -25 -15 -33  19  15

Again, let’s extend to |-1|-2|-3|-4|-5|-6|-7|.

AbsValue7 <- function(v,m=1){
  if(length(v)!=7){return("Error")}
  else{
     A  <- AbsValue5(v[1:5])+v[6]*abs(v[7])
     a1 <- m*abs(v[1])+v[2]*abs(v[3])+v[4]*abs(v[5]*abs(v[6])+v[7])
     a2 <- m*abs(v[1]*abs(v[2])+v[3])+v[4]*abs(v[5]*abs(v[6])+v[7])
     a3 <- m*abs(v[1])+v[2]*abs(AbsValue3(v[4:6],v[3])+v[7])
     a4 <- m*abs(AbsValue5(v[2:6],v[1])+v[7])
  }
  return(sort(c(A,a1,a2,a3,a4)))
}

## Check our work
AbsValue7(-(1:7))
##  [1] -169 -153 -143  -97  -75  -67  -57  -27  -23   39   47   47   51   87

One more time, and we will get our answer. We extend to |-1|-2|-3|-4|-5|-6|-7|-8|-9|.

AbsValue9 <- function(v,m=1){
  if(length(v)!=9){return("Error")}
  else{
     A  <- AbsValue7(v[1:7])+v[8]*abs(v[9])
     a1  <- m*abs(v[1])+v[2]*abs(v[3])+v[4]*abs(v[5])+v[6]*abs(v[7]*abs(v[8])+v[9])
     a2  <- m*abs(v[1]*abs(v[2])+v[3])+v[4]*abs(v[5])+v[6]*abs(v[7]*abs(v[8])+v[9])
     a3  <- m*abs(v[1])+v[2]*abs(v[3]*abs(v[4])+v[5])+v[6]*abs(v[7]*abs(v[8])+v[9])
     a4 <- m*abs(v[1])+v[2]*abs(v[3])+v[4]*abs(AbsValue3(v[6:8],v[5])+v[9])
     a5 <- m*abs(AbsValue3(v[2:4],v[1])+v[5])+v[6]*abs(v[7]*abs(v[8])+v[9])
     a6 <- m*abs(v[1]*abs(v[2])+v[3])+v[4]*abs(AbsValue3(v[6:8],v[5])+v[9])
     a7 <- m*abs(v[1])+v[2]*abs(AbsValue5(v[4:8],v[3])+v[9])
     a8 <- m*abs(AbsValue7(v[2:8],v[1])+v[9])
  }
  return(sort(unique(c(A,a1,a2,a3,a4,a5,a6,a7,a8))))
}

## What are the values and how many are there? 
RiddleVals <- AbsValue9(-(1:9))
length(RiddleVals)
## [1] 39
RiddleVals
##  [1] -1041 -1031  -953  -541  -437  -423  -415  -405  -385  -375  -371
## [12]  -285  -241  -225  -215  -213  -169  -147  -139  -129   -99   -95
## [23]   -33   -25   -21    15    25    33    85    93   105   115   141
## [34]   171   187   221   269   273   479