# project euler — problem 68

December 12, 2012
By

(This article was first published on YGC » R, and kindly contributed to R-bloggers)

Consider the following "magic" 3-gon ring, filled with the numbers 1 to 6, and each line adding to nine.

Working clockwise, and starting from the group of three with the numerically lowest external node (4,3,2 in this example), each solution can be described uniquely. For example, the above solution can be described by the set: 4,3,2; 6,2,1; 5,1,3.

It is possible to complete the ring with four different totals: 9, 10, 11, and 12. There are eight solutions in total.

Total      Solution Set
9        4,2,3; 5,3,1; 6,1,2
9        4,3,2; 6,2,1; 5,1,3
10      2,3,5; 4,5,1; 6,1,3
10      2,5,3; 6,3,1; 4,1,5
11      1,4,6; 3,6,2; 5,2,4
11      1,6,4; 5,4,2; 3,2,6
12      1,5,6; 2,6,4; 3,4,5
12      1,6,5; 3,5,4; 2,4,6
By concatenating each group it is possible to form 9-digit strings; the maximum string for a 3-gon ring is 432621513.

Using the numbers 1 to 10, and depending on arrangements, it is possible to form 16- and 17-digit strings. What is the maximum 16-digit string for a "magic" 5-gon ring?

----
This is a very interesting problem, it tooks me several hours to solve it. There are many details need to consider.

Firstly, the nodes are divided into inNode and outNode. I defined getNode to generate all the combination of inNode and outNode.

The inNode set (iv) was used to generate all the possible combination of innode groups. All the possible sums of the innode group and outnode were further calculated. Only those have a same sum were retained.

If these innode groups have more than one combination, iterate all the possible combinations. Each combination (innode.sel) should passed the criteria of each innode was used two times, and sorted by inNodeSort, which make sure that one time the innode was used in the middle, the other time it should be used in the end, or vice versa.

The innode group and outnode were then combined to form a matrix, this matrix was sorted to let the gon-ring starting from the lowest outnode, and the path is smallest. If the rowSums of this matrix is unique, it means one of the possible gon-ring solution was found. Then concatenating each number to form a digit string.

?View Code RSPLUS
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132  getNode <- function(num, n) { inNode <- t(combn(num, n)) outNode <- sapply(1:nrow(inNode), function(i) num[!num %in% inNode[i,]]) outNode <- t(outNode) s <- rowSums(inNode) * 2 + rowSums(outNode) idx <- s %% n == 0 inNode <- inNode[idx,] outNode <- outNode[idx,] res <- list(inNode=inNode, outNode=outNode) return(res) }   getGonRingDigit <- function(iv, ov){ innode <- t(combn(iv,2)) n <- length(ov)   cs <- outer(rowSums(innode), ov, "+") cnt <- table(cs) cnt <- cnt[cnt >= n] s <- as.numeric(names(cnt))   result <- c() for (x in s) { ss <- apply(cs, 2, function(i) which(i==x)) len <- sapply(ss, length) if (any(len == 0)) { next }   idx <- t(combn(unlist(ss), n)) ii <- apply(idx, 1, function(i) { all(colSums(sapply(ss, function(j) i %in% j)) == 1) }) idx <- idx[ii,] nr <- nrow(idx) if (is.null(nr)) { idx <- matrix(idx, nrow=1) nr <- nrow(idx) } if ( length(nr) == 0) { next }   for(i in 1:nr) { innode.sel <- innode[idx[i,],] if (any(table(innode.sel) != 2)) { next } innode.sel <- inNodeSort(innode.sel) outnode <- sort(ov, decreasing=T) result <- c(result, gonRingNumber(innode.sel, outnode)) } } if (is.null(result)) { result <- NA } return(result) }   inNodeSort <- function(innode) { innode <- innode[order(rowSums(innode)),] iicache <- c() for (j in 1:ncol(innode)) { repeat { count <- table(innode[,j]) dup <- names(count[count > 1]) if(is.null(dup) || is.na(dup) || length(dup) == 0) { break } iis <- which(innode[,j] == dup[1]) if (iis[2] %in% iicache) { ii <- iis[1] iicache <- c(iicache, ii) } else { ii <- iis[2] iicache <- c(iicache,ii) } innode[ii,] <- rev(innode[ii,]) } } return(innode) }   setGonOrder <- function(d) { dd <- d[order(d[,1]),] for (i in 2:(nrow(dd)-1)) { j <- which(dd[, ncol(dd)-1] == dd[(i-1), ncol(dd)]) if (length(j) != 0 && !is.na(j)) { tmp <- dd[i,] dd[i,] <- dd[j,] dd[j,] <- tmp } } return(dd) }   gonRingNumber <- function(innode, outnode) { res <- character(2) d <- cbind(outnode, innode) if (length(unique(rowSums(d))) == 1 ) { res[1] <- gonRingNumber_internal(d) } else { res[1] <- NA } d <- cbind(innode, outnode) d <- d[,rev(1:ncol(d))] if (length(unique(rowSums(d))) == 1 ) { res[2] <- gonRingNumber_internal(d) } else { res[2] <- NA } return(res) }   gonRingNumber_internal <- function(d) { d <- setGonOrder(d) n <- as.numeric(t(d)) res <- paste(n, collapse="") return(res) }   num <- 1:10 n <- 5 node <- getNode(num,n) inNode <- node$inNode outNode <- node$outNode idx <- apply(inNode, 1, function(i) ! 10 %in% i) inNode <- inNode[idx,] outNode <- outNode[idx,] res <- lapply(1:nrow(inNode), function(i) getGonRingDigit(inNode[i,], outNode[i,])) cat("Answer of PE 68:", max(unlist(res), na.rm=TRUE), "\n")

This code run in less than 0.1 second.

> system.time(source("problem68.R"))
user  system elapsed
0.097   0.000   0.096