// For every way to order the polygonal types, perform depth-first search, // returning as soon as a solution is found. func problem61() int { polys := allFourDigitPolygons() perms := tools.Permutations([]int{0, 1, 2, 3, 4, 5}, 6) for _, perm := range perms { // We will build up candidate solutions incrementally. The // starting nodes are therefore the members of polys[perm[0]]. var frontier tools.Stack for _, p := range polys[perm[0]] { frontier.Push(Node{p}) } for len(frontier) > 0 { n := frontier.Pop().(Node) z := len(n) if z == 6 && isAllCyclic(n) { return tools.Sum(n...) } else if z < 6 { // Note that perm[z] contains the next // polygonal type in this order. for _, y := range polys[perm[z]] { if isCyclic(n[z-1], y) { child := append(append(Node{}, n...), y) frontier.Push(child) } } } } } return -1 // Unreachable because there is a known solution. }
func problem68() int { const start = 6 // Each line cannot sum to less than 6 (1+2+3) const end = 27 // or greater than 27 (8+9+10) // Use a depth-first search. var frontier tools.Stack var solutions [][]int // Populate the initial frontier. for N := start; N <= end; N++ { for i := 1; i <= 10; i++ { r := Ring{ solution: []int{i}, used: make(map[int]bool), N: N, } r.used[i] = true frontier.Push(r) } } for len(frontier) > 0 { r := frontier.Pop().(Ring) if len(r.solution) == 15 { // Each solution can be described uniquely starting from the // group of three with the numerically lowest external node and // working clockwise. So we need to filter for solutions where // a < min(d, f, h, j) s := r.solution if s[0] < tools.Min(s[3], s[6], s[9], s[12]) { solutions = append(solutions, s) } } for _, child := range successors(r) { frontier.Push(child) } } // Process solutions according to the problem spec. var strlen = 16 var answers []int for _, sol := range solutions { var ss []string for _, x := range sol { ss = append(ss, strconv.Itoa(x)) } s := strings.Join(ss, "") if len(s) == strlen { val, _ := strconv.Atoi(s) answers = append(answers, val) } } return tools.Max(answers...) }
func problem75() int { limit := 1500000 // A mapping from values of L to the number of right-angles triangles // with the perimiter L. triangles := make(map[int]int) // Use a depth-first search to exhaust the search space, starting with // the first Pythagorean triple. var frontier tools.Stack frontier.Push(Triple{3, 4, 5}) for len(frontier) > 0 { t := frontier.Pop().(Triple) L := tools.Sum(t...) if L > limit { continue } triangles[L]++ // We're not only interested in 'primitive triples', but // multiples too a, b, c := t[0], t[1], t[2] for i := 2; ; i++ { multiple := Triple{i * a, i * b, i * c} if tools.Sum(multiple...) >= limit { break } triangles[tools.Sum(multiple...)]++ } for _, child := range t.children() { frontier.Push(child) } } count := 0 for _, val := range triangles { if val == 1 { count++ } } return count }
// We are going to use a concurrent depth-first search with a worker goroutine // pool of 4. Each goroutine will search for a solution from a different // starting prime. As soon as a solution is found, we return from the function. // Otherwise, we wait for all starting primes to be checked, and return an // error. func problem60() (int, error) { // It's not clear how many primes to search through. Experimentation // suggests a limit of 9000 produces the correct answer: 26033. Note // our algorithm does not guarantee the solution is the smallest // possible, but as a matter of fact, it is. We could verify our // answer by raising the limit to 26033, searching exhaustively, and // observing that no smaller solutions are found. limit := 9000 var primes []int for i := 0; i < limit; i++ { if tools.IsPrime(i) { primes = append(primes, i) } } c := newCache() ans := make(chan int) // Used to send the answer. done := make(chan bool) // Used to signal that all worker goroutines are done. pchan := make(chan int) // Used to send worker goroutines a starting prime to search. var wg sync.WaitGroup // Woker goroutine pool of 4. for i := 0; i < 4; i++ { wg.Add(1) go func() { for { p, ok := <-pchan if !ok { wg.Done() return } // Perform depth-first search starting at the given prime. var frontier tools.Stack frontier.Push(Node{p}) for len(frontier) != 0 { n := frontier.Pop().(Node) if len(n) == 5 { ans <- tools.Sum(n...) } for _, prime := range primes { child := append(append(*new(Node), n...), prime) if prime > tools.Max(n...) && c.allConcatToPrime(child) { frontier.Push(child) } } } } }() } go func() { for _, p := range primes { pchan <- p } close(pchan) wg.Wait() // Wait for all workers to complete their search done <- true // before sending completion signal. }() for { select { case x := <-ans: return x, nil case <-done: return -1, fmt.Errorf("problem60: no solution found with limit %v", limit) } } }