Example #1
0
// 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.
}
Example #2
0
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...)
}
Example #3
0
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
}
Example #4
0
// 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)
		}
	}
}