object Euler38 extends Application {
def ispan (n :String) = n.toList.sort(_<_).mkString == "123456789";
def catprod (n :Int, r :Int) = List.range(1, r+1).flatMap(p => (n*p).toString.toList)
def find (n :Int, r :Int, max :Int) :Int = {
val cp = catprod(n, r);
if (cp.length > 9)
if (r == 2) max
else find(n, r-1, max);
else
if (ispan(cp)) find(n+1, r, Math.max(cp.mkString.toInt, max))
else find(n+1, r, max);
}
println(find(1, 5, 0));
}
I wrote the above general solution which computes the concatenated product with (1, 2, 3, 4, 5) for increasing values of n until the product exceeds 9 digits, then moves down to (1, 2, 3, 4) and so on. Once I had my answer, I read the forums and discovered that a bit of thinking would have led me to the knowledge that the solution had to be a four digit value multiplied by (1, 2). Even less thinking would have made me realize that the solution had to be larger than the example given in the problem statement. Fortunately I did none of that thinking and got to write an interesting algorithm. A more thoughtful me might have written the following:
I cheated a little and turned the integer into a string in order to easily truncate it from the left, it would perhaps have been more elegant to take the value modulo 10digits-1 but the code would have been longer and I feel an irrational desire for brevity in these solutions.
Coming up with rotate() was fun and the rest was pretty straightforward. I first wrote a recursive method that rotated the number one digit at a time, but then I realized I could do it in a single expression. If there existed a built in operator for raising an integer to an integer power, rotate would look much nicer.
object Euler34 extends Application {
val facts = Array(1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880);
def digfact (sum :Int, n :Int) :Int =
if (n == 0) sum else digfact(facts(n % 10) + sum, n/10);
def check (sum :Int, n :Int) :Int =
if (n == 2000000) sum;
else if (digfact(0, n) == n) check(sum+n, n+1);
else check(sum, n+1);
println(check(0, 10));
}
Since we’re only computing factorial for single digit numbers, we pre-compute the values of 0 through 9 factorial and then breeze up through the positive integers accumulating all numbers that meet our criterion. We stop searching at 2000000 because 1999999 is the last number for which the sum of the factorials of the digits is greater than or equal to the number itself.
Nothing too clever here. I did realize that cancellable fractions would either be kn/mk or nk/km, and fortunately the product reduces to 1/100 so determining the denominator is simple.
object Euler32 extends Application {
def digits (n :Int) = n.toString.toList.map(c => c - '0');
def ispan (a :Int, b :Int, n :Int) :Boolean =
(digits(a) ::: digits(n) ::: digits(b)).sort(_<_).equals(List.range(1,10));
def haspanfact (n :Int) :Boolean =
List.range(2, 100).find((a) => (n % a == 0 && ispan(a, n/a, n))) != None;
println(List.range(1000, 10000).filter(haspanfact).foldRight(0)(_+_));
}
We want a factorization of a four digit number (which will be either two digits times three or one digit times four), so we enumerate all 4 digit numbers from 1000 to 9999, filter them by checking all possible one and two digit divisors first for divisibility and second for pandigitality, then sum the matches. Being able to decompose numbers into lists of digits, jam those lists together, sort them and compare them to a list with 1 through 9 in consecutive order in three lines of code sure is handy.
Nothing too fancy here, just enumerate all legal quantities of the first coin in the list (including zero), then recurse with that coin removed from the list and its value times the number chosen removed from the sum.
A decent balance of compactness and readability, IMHO. I would like to do away with the 200,000 upper limit which I came to via some preparatory noodling.
The bold numbers are the values for start and the step is the distance from the start position to the first corner of the next ring and from that corner to the next corner.
It was a bit too Dead Poets Society at times, but the characters were likable and their circumstances not so contrived as to push one beyond the ability to suspend disbelief. I liked the portrayal of enthusiasm for learning, not so much the investigation of the morality of subjugating that passion to get into Cambridge. Fortunately the rest of the film made up for the unenjoyable main plot line.
object Euler27 extends EulerApp {
val MAX_N = 80;
val primes = genprimes(MAX_N*MAX_N + MAX_N*1000 + 1000);
def polyprimes (primes :Array[Int], a :Int, b :Int, n :Int) :Int =
if (primes(Math.abs(n*n + a*n + b)) == 0) 0 else 1 + polyprimes(primes, a, b, n+1);
val polys = for {
a <- List.range(-1000, 1000)
b <- List.range(-1000, 1000)
p <- List(polyprimes(primes, a, b, 0))
if (p > 0)
} yield Pair(a * b, p);
println(polys.foldLeft(Pair(0, 0))((a, b) => if (a._2 > b._2) a else b)._1);
}
Pretty straightforward. Just try all combinations from -1000 to 1000 and see how many consecutive primes are generated and pick the one that generates the most. You may notice that I’m passing the list of primes into the polyprimes function because referencing it directly from its enclosing context annoyingly results in a 40 second runtime instead of 28 seconds.
object Euler26 extends Application {
def divcycle (numer :Int, denom :Int, rlist :List[Int]) :Int = {
val remain = numer % denom;
if (remain == 0) return 0;
val ridx = rlist.indexOf(remain);
if (ridx >= 0) ridx+1 else divcycle(remain * 10, denom, remain :: rlist);
}
var cycles = List.range(1, 1000).map(v => divcycle(1, v, Nil));
println(cycles.indexOf(cycles.foldRight(0)(Math.max))+1);
}
This one turned out to be easy once I realized that the key was looking at the remainder after each step in the long division process. If the remainder is ever the same as a remainder you’ve seen previously, then you’ve found a loop.
Prior to that I was tracking the actual division result and looking for loops in the digits which was problematic because one doesn’t know when to declare a repeated series of numbers a loop. I was in the middle of doing some hairbrained fiddling that involved making sure that the repeated series of digits was at least as long as all the digits that had come before it when I realized there must be a more elegant solution. Indeed there was.