-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem 5: smallest positive number divisible by all of the numbers from 1 to 20 #10
Comments
and here's a one-liner: (1 to 20).map(primeFactors(_)).reduce((D1, D2) => D1 ++ D2.diff(D1)).product It uses the explanation with the prime numbers, given in the problem statement.
Then we reduce it, adding up the different Vectors, but we only add the elements of the next Vector, that are not yet present in the previous result Vector:
and so on. |
And here's an other solution, that doesn't use the prime numbers explanation, given in the problem statement. It uses a function (isDivisibleBy(1), isDivisibleBy(2), .., isDivisibleBy(20)) Then we filter a |
I solved it this way - maybe not the fanciest but it works. final def testMultimplesOf20(magicNumber : Int,
nrsToDivBy : List[Int], multiplicationFactor : Int): Int = {
if(nrsToDivBy.filter(x => magicNumber%x == 0).size == nrsToDivBy.size) {
magicNumber
} else {
testMultimplesOf20(20*multiplicationFactor, nrsToDivBy, multiplicationFactor+1)
}
}
euler(problem(5)) {
testMultimplesOf20(20, List.range(2, 21), 1)
} |
Hi Jamie, If you move testMultimplesOf20 to the scope of your specific test such as follows: euler(problem(5), "jamie") {
} … all is well, compiles and doesn’t produce a stack overflow. Must have something to do with moving testMultimplesOf20 out of the higher scope. Haven’t figured out why this is. When you leave testMultimplesOf20 in the location where you put it and add @annotation.tailrec just in front of it, the following compilation error message is produced: [error] /Users/ericloots/Dropbox/Scala/ScalaCronosBlogs/EulerHackingSession/project-euler/src/test/scala/org/bescala/projecteuler/problems/Problem005.scala:96: could not optimize @tailrec annotated method testMultimplesOf20: it is neither private nor final so can be overridden Guess that this is linked in some way to the stack overflow problem... Haven’t understood how your solution work :-) but it seems to work (except that it’s quite slow :-) You may wish to label your solutions as marked in red above. In this way you can easily play with different versions. Regards, Eric
|
Hi Eric
|
@jamiephillips0000 It stack overflows because, well, it stack overflows. Although the method is tail recursive, the computer cannot optimise it to a while loop, because the method can potentially be overriden in a subclass. So the compiler doesn't generate a loop but just generates recursive calls. And each function call consumes some stack memory at run time. Making it final, or define it inside a block, the compiler is sure it cannot be overriden and hence can optimise it to an iteration. And it happens that this solution generates a very deep recursion, so deep the stack overflows... You could experiment with a JVM option to allow a bigger stack, but in general it sounds like a not the best option. Making it final or turning it into a nested (unoverridable) function is better. |
Take a bow Sam de Becker!! ;-) |
What is happening is that you can't do tail recursive optimization on methods that can be overridden. The compiler will not optimize it and you get a stack overflow because you are indeed building a deep stack. |
@samdebacker does gave a much detailed explanation! |
The pencil and paper solution
and 3 generic solutions (1, 2 and 3 ) using I show intermediate values of the calculation in comment in the first two implementations. Maybe useful for some: note the distribution of steps over multiple lines. In this way you can avoid one-liners that require horizontal scrolling ;-) -and- you create an opportunity to document/annotate the intermediate steps/results. Note that solution 3 is a simplification of solution 1. I realized that the simplification was possible after reading the explanation by Michel Verbist of his generic solution. |
Hi, @mverbist, your solution is essentially the same as mine I use my algorithm reduces an intermediate vector of vectors:
to
using Cheers Luc |
Following up on the comments in the last meet-up and posts here, I came up with an explicit example of why the compiler can't optimise the tail recursion in a method that's not final. Take a look at this snippet: class A { class B extends A { println((new B).notReallyTailRec(5)) If you run it in the REPL, you'll get this output: In B: 5 As you can see, you really can arrange for the method A.notReallyTailRec() to call an overridden implementation in a subclass. |
This morning I published the code from the last session. Here goes the comments related to Problem005. I decided to keep it in separated commits so one can see and analyse the evolution of the code. The first commit labelled "Problem005" only adds the solutions as proposed by Vincent, Michel and Luc. Next there are 4 commits prefixed with "P005". P005: removing unnecessary refs to immutable vals P005: using default values P005: extracting boolean from the mix and using pattern matching The main code inside isDivisbleAcc is replaced by a pattern matching on the list of 'dividends'. The goal was to show how we can pattern matching on a List. P005: making it tailrec |
Using the maxima of the powers of prime factors dividing the numbers from 1 to 20 mySolution.
The text was updated successfully, but these errors were encountered: