Site icon R-bloggers

project euler — problem 68

[This article was first published on YGC » R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

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"))
Answer of PE 68: 6531031914842725 
   user  system elapsed 
  0.097   0.000   0.096 

Related Posts

To leave a comment for the author, please follow the link and comment on their blog: YGC » R.

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.