func (c *cache) concatsToPrime(x, y int) bool { key := strconv.Itoa(x) + strconv.Itoa(y) c.Lock() defer c.Unlock() // Try to find the answer in the cache. if val, ok := c.m[key]; ok { return val } // Otherwise find it manually and add to the cache. xstr, ystr := strconv.Itoa(x), strconv.Itoa(y) a, _ := strconv.Atoi(xstr + ystr) b, _ := strconv.Atoi(ystr + xstr) val := tools.IsPrime(a) && tools.IsPrime(b) c.m[key] = val return val }
// The search space is too large for brute-force. So, note that we are seeking // roughly the inverse of the previous problem -- to minimize n/phi(n). // Therefore, we want to maximize phi(n), which is acheived for numbers with // the fewest and largest unique prime factors. But the number cannot simply be // prime because in that case phi(n) == n-1 which is not a permutation of n. // Therefore, the best candidates should have two unique prime factors. func problem70() int { // Since we are seeking large values for both prime factors, we can // search among numbers close to the value of sqrt(1e7) ~ 3162 var primes []int for i := 2000; i < 4000; i++ { if tools.IsPrime(i) { primes = append(primes, i) } } // Keep track of the minimal n/phi(n) ratio found // and the value of n that produced it. min := math.Inf(1) res := 0 const limit = 10000000 for _, x := range primes { for _, y := range primes { if n := x * y; n < limit && x != y { if pn := tools.Phi(n); isPerm(n, pn) { if rat := float64(n) / float64(pn); rat < min { min = rat res = n } } } } } return res }
// Return a new slice holding only the elements of xs that are prime. func filterPrime(xs []int) []int { var ys []int for _, x := range xs { if tools.IsPrime(x) { ys = append(ys, x) } } return ys }
// problem51 loops over ints from 56995 to inf, returning the smallest member // of an 8-prime family. func problem51() int { n := 56995 // given in the description as a member of a 7-prime family for { if tools.IsPrime(n) && isSmallestMember(n) { return n } n++ } }
// Note that the phi function multiplies n by (1 - (1/p)) for every p in // its unique prime factors. Therefore, phi(n) will diminish as n has a greater // number of small unique prime factors. Since we are seeking the largest value // for n/phi(n), we want to minimize phi(n). We are therefore looking for the // largest number <= 1e6 which is the product of the smallest unique prime // factors, i.e successive prime numbers starting from 2. func problem69() int { limit := 1000000 candidate := 1 // The multiplicative identity. for i := 2; i*candidate <= limit; i++ { if tools.IsPrime(i) { candidate *= i } } return candidate }
// isSmallestMember checks whether the given number satisfies the problem // description: is it the smallest member of an 8-prime family? func isSmallestMember(n int) bool { for _, indices := range findIndices(n) { var primes []int for _, x := range family(n, indices) { if tools.IsPrime(x) { primes = append(primes, x) } } if len(primes) == 8 { return true } } return false }
func problem77() int { var primes []int for i := 2; i < 100; i++ { if tools.IsPrime(i) { primes = append(primes, i) } } // What is the first value which can be written as the sum of primes in // over five thousand different ways? for i := 2; ; i++ { if x := numPartitions(i, primes); x > 5000 { return i } } }
// 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) } } }