Skip to content

Instantly share code, notes, and snippets.

@mmahmoudian
Last active December 16, 2019 13:56
Show Gist options
  • Select an option

  • Save mmahmoudian/32dbd85442e74e93b733d4fe48beda1e to your computer and use it in GitHub Desktop.

Select an option

Save mmahmoudian/32dbd85442e74e93b733d4fe48beda1e to your computer and use it in GitHub Desktop.

Revisions

  1. mmahmoudian revised this gist Dec 16, 2019. 1 changed file with 209 additions and 58 deletions.
    267 changes: 209 additions & 58 deletions which_square_of_sums.R
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,10 @@
    ## This code is in response to the following tweet of "Fermat's Library" ##
    ## https://twitter.com/fermatslibrary/status/1180837639915819008?s=20 ##
    ## ##
    ## It is also corresponsing to the following entry in the On-Line ##
    ## Encyclopedia of Integer Sequences (OEIS): ##
    ## https://oeis.org/search?q=A104113 ##
    ## ##
    ## Author: Mehrad Mahmoudian ##
    ## License: GPL3 ##
    ## ##
    @@ -22,78 +26,225 @@
    ################################################################################


    func_cutter <- function(x, cuts){
    ## Description:
    ## A function to cut a string at given points
    ##
    ## Arguments:
    ## x: A character vector of length 1
    ## cuts: a numeric vector of length smaller than nchar(x)
    #-------[ load packages ]-------#
    {
    library("parallel")
    library("foreach")
    library("doParallel")
    }


    #-------[ initial settings ]-------#
    {
    ## what range of numbers should it cover
    lower_bound <- 10
    upper_bound <- 100000000


    # create a collective variable
    final <- c()
    # how many tasks should we push to each CPU core (this depends on the processing
    # power of each core and the total available memory, so don't set it too high
    jumps <- 250

    # go through all the cutting points that is provided to the function
    for(i in seq_along(cuts)){
    setwd("~/tmp/A104113/")
    }


    #-------[ internal functions ]-------#
    {
    func_cutter <- function(x, cuts){
    ## Description:
    ## A function to cut a string at given points
    ##
    ## Arguments:
    ## x: A character vector of length 1
    ## cuts: a numeric vector of length smaller than nchar(x)

    # if the index is 1
    if(i == 1){
    # append the first few chracters as an item to the collective vector
    final <- c(final, substr(x = x, start = 1, stop = cuts[i]))
    }
    # create a collective variable
    final <- c()

    # if we got to the end of the cutting points list
    if(i == length(cuts)){
    # set the ending to be the end of the string
    tmp_end_index <- nchar(x)
    }else{
    # using the next cutting point as the ending position
    tmp_end_index <- cuts[i+1]
    # go through all the cutting points that is provided to the function
    for(i in seq_along(cuts)){

    # if the index is 1
    if(i == 1){
    # append the first few chracters as an item to the collective vector
    final <- c(final, substr(x = x, start = 1, stop = cuts[i]))
    }

    # if we got to the end of the cutting points list
    if(i == length(cuts)){
    # set the ending to be the end of the string
    tmp_end_index <- nchar(x)
    }else{
    # using the next cutting point as the ending position
    tmp_end_index <- cuts[i+1]
    }

    final <- c(final, substr(x = x, start = cuts[i]+1, stop = tmp_end_index))
    }

    final <- c(final, substr(x = x, start = cuts[i]+1, stop = tmp_end_index))
    return(final)
    }

    return(final)

    chunk2 <- function(x,n){
    ## source: https://stackoverflow.com/a/16275428/1613005
    ##
    ## Description:
    ## A function to break the provided vector into n equal smaller vectors
    ##
    ## Arguments:
    ## x: a vector that you want to cut into chunks
    ## n: number of chunks

    split(x,
    cut(seq_along(x),
    n,
    labels = FALSE))
    }
    }


    # iterate through the range
    for(i in 10:100000){
    # iterate through the possible number of cuts we can have to the string
    for(j in seq_len(nchar(i)-1)){
    # generate all possible combination of cutting positions considering the
    # constraint on total number of cuts allowed
    combinations <- apply(combn(x = nchar(i)-1, m = j), 2, function(a){
    func_cutter(x = i, cuts = a)
    })

    #-------[ ininital setup ]-------#
    {
    # set the number to maximum possible but leave one out for the grace
    parallel.cores <- parallel::detectCores() - 1

    ## register parallel backend
    cl <- parallel::makeCluster(parallel.cores, outfile = "")
    doParallel::registerDoParallel(cl)

    }


    #-------[ main ]-------#
    {
    # create an empty vector to be filled in the loop below
    final <- c()

    # initiate the start of i in the loop
    i_low <- lower_bound


    ## initiate the result file with a separator tagged with date and time
    header_txt <- paste0("[ ", format(Sys.time(), "%Y%m%d-%H%M%S"), " ]")
    padding_length <- (80 - nchar(header_txt)) / 2
    header_txt <- paste0(paste(rep.int("-", ceiling(padding_length)), collapse = ""),
    header_txt,
    paste(rep.int("-", floor(padding_length)), collapse = ""))
    cat(header_txt, file = "result.txt", sep = "\n", append = T)



    # run as long i is less than the upper bound
    while(i_low <= upper_bound){
    # define how high we want to go in this round of while
    i_high <- i_low + (parallel.cores * jumps)

    # go through all generated pieces and do the math
    apply(combinations, 2, function(x){
    x_numeric <- as.numeric(x)

    # if the square of sums was equal to the number we are testing
    if((sum(x_numeric)^2) == i){
    # print the combination
    cat(paste0("(", paste0(x, collapse = "+"), ")^2"), "=", i, "\n")
    }
    })
    # if the hight we defined is larger than the user-defined upper bound
    if(i_high > (upper_bound + 1)){
    # trim it to be as high as upper bound +1 (+1 is to make the while loop break)
    i_high <- upper_bound + 1
    }

    # break the distance between i_low to i_high to equal chunks where chunk count is equal to parallel.cores
    chunks <- chunk2(i_low:i_high, parallel.cores)

    cat(i_low, ":", i_high, ":: ")


    # got through the numbers between the lower and upper limits of this round of while
    tmp_final <- foreach(ii = chunks,
    .inorder = TRUE,
    .combine = c) %dopar% {

    tmp_outer_collective <- c()

    for(i in ii){

    tmp_inner_collective <- c()

    # iterate through the possible number of cuts we can have to the string
    for(j in seq_len(nchar(i) - 1)){
    # generate all possible combination of cutting positions considering the
    # constraint on total number of cuts allowed
    combinations <- apply(combn(x = nchar(i) - 1, m = j), 2, function(a){
    func_cutter(x = i, cuts = a)
    })


    # go through all generated pieces and do the math
    for(k in 1:ncol(combinations)){
    # select this specific combination
    x <- as.character(combinations[, k])

    # turn this combination into numbers
    x_numeric <- as.numeric(x)

    # if the square of sums was equal to the number we are testing
    if((sum(x_numeric)^2) == i){
    tmp_success <- paste0("(", paste0(x, collapse = "+"), ")^2")

    tmp_inner_collective <- c(tmp_inner_collective, tmp_success)
    }
    }
    }


    # if the collective variable was not empty
    if(length(tmp_inner_collective)){
    # retusn a character vector of length 1 with correct desired format
    tmp_inner_collective <- paste0(paste0("[",
    format(Sys.time(),
    "%Y%m%d-%H%M%S"),
    "]\t"),
    i,
    "\t::\t",
    paste(tmp_inner_collective,
    collapse = ", "))
    }else{
    tmp_inner_collective <- NULL
    }

    tmp_outer_collective <- c(tmp_outer_collective, tmp_inner_collective)

    }


    return(tmp_outer_collective)

    }


    # append to the global collective1 variable
    final <- c(final, tmp_final)

    if(!is.null(tmp_final)){
    cat(length(tmp_final), "\n")
    cat(tmp_final, file = "result.txt", sep = "\n", append = T)
    }else{
    cat("0\n")
    }

    # put the highest I we covered to be the lowest to get ready for the next round of while loop
    i_low <- i_high

    invisible(gc())
    }

    parallel::stopCluster(cl)

    invisible(gc())
    }

    # (8+1)^2 = 81
    # (10+0)^2 = 100
    # (1+29+6)^2 = 1296
    # (20+25)^2 = 2025
    # (30+25)^2 = 3025
    # (6+72+4)^2 = 6724
    # (8+2+81)^2 = 8281
    # (82+8+1)^2 = 8281
    # (98+01)^2 = 9801
    # (98+0+1)^2 = 9801
    # (100+00)^2 = 10000
    # (100+0+0)^2 = 10000
    # (5+5+225)^2 = 55225
    # (88+209)^2 = 88209

    #-------[ save the results ]-------#
    {
    comment(final) <- paste("From", lower_bound, "to", upper_bound)

    saveRDS(object = final,
    file = paste0(paste(format(Sys.time(), "%Y%m%d-%H%M%S"), "final", lower_bound, "to", upper_bound, sep = "_"), ".RDS"))

    }

  2. mmahmoudian created this gist Nov 8, 2019.
    99 changes: 99 additions & 0 deletions which_square_of_sums.R
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    ################################################################################
    ## This code is in response to the following tweet of "Fermat's Library" ##
    ## https://twitter.com/fermatslibrary/status/1180837639915819008?s=20 ##
    ## ##
    ## Author: Mehrad Mahmoudian ##
    ## License: GPL3 ##
    ## ##
    ## Description: ##
    ## The in the aforementioned tweet it is mentioned that 81 is the only ##
    ## number that has (8+1)^2=81 feature. I thought it is a nice morning ##
    ## coding challenge. From that tweet either of these conditions can be ##
    ## deduced: ##
    ## - if you treat the number as a string and cut it in half, the square ##
    ## of sums should be equal to the number. ##
    ## - if you treat the number as a string and split it into individual ##
    ## digits, the square of sums should be equal to the number ##
    ## - if you treat the number as a string and break it at some point(s), ##
    ## the square of sums should be equal to the number. ##
    ## ##
    ## I realized the last one is the most complex one and also it contains the ##
    ## other two in itself, so I challenged myself :D ##
    ################################################################################


    func_cutter <- function(x, cuts){
    ## Description:
    ## A function to cut a string at given points
    ##
    ## Arguments:
    ## x: A character vector of length 1
    ## cuts: a numeric vector of length smaller than nchar(x)

    # create a collective variable
    final <- c()

    # go through all the cutting points that is provided to the function
    for(i in seq_along(cuts)){

    # if the index is 1
    if(i == 1){
    # append the first few chracters as an item to the collective vector
    final <- c(final, substr(x = x, start = 1, stop = cuts[i]))
    }

    # if we got to the end of the cutting points list
    if(i == length(cuts)){
    # set the ending to be the end of the string
    tmp_end_index <- nchar(x)
    }else{
    # using the next cutting point as the ending position
    tmp_end_index <- cuts[i+1]
    }

    final <- c(final, substr(x = x, start = cuts[i]+1, stop = tmp_end_index))
    }

    return(final)
    }


    # iterate through the range
    for(i in 10:100000){
    # iterate through the possible number of cuts we can have to the string
    for(j in seq_len(nchar(i)-1)){
    # generate all possible combination of cutting positions considering the
    # constraint on total number of cuts allowed
    combinations <- apply(combn(x = nchar(i)-1, m = j), 2, function(a){
    func_cutter(x = i, cuts = a)
    })

    # go through all generated pieces and do the math
    apply(combinations, 2, function(x){
    x_numeric <- as.numeric(x)

    # if the square of sums was equal to the number we are testing
    if((sum(x_numeric)^2) == i){
    # print the combination
    cat(paste0("(", paste0(x, collapse = "+"), ")^2"), "=", i, "\n")
    }
    })
    }
    }

    # (8+1)^2 = 81
    # (10+0)^2 = 100
    # (1+29+6)^2 = 1296
    # (20+25)^2 = 2025
    # (30+25)^2 = 3025
    # (6+72+4)^2 = 6724
    # (8+2+81)^2 = 8281
    # (82+8+1)^2 = 8281
    # (98+01)^2 = 9801
    # (98+0+1)^2 = 9801
    # (100+00)^2 = 10000
    # (100+0+0)^2 = 10000
    # (5+5+225)^2 = 55225
    # (88+209)^2 = 88209