2

I am trying to solve the following (Project Euler) problem using R (and iterators and the foreach package),

What is the smallest positive number that is divisible by all of the numbers from one to fifteen?

and while I think that my code should do the job, it does not seem to:

library(foreach)
library(iterators)

# function to check sequence of natural numbers for  
#   divisibility by a given list of factors
fnDivision = function(maxNum, vFactors) {
  foreach(i = icount(factorial(15))) %do% {
    if(!i %% 100 ) cat(sprintf("Done with the first %i natural numbers.\n", i))
    if(all(! i %% vFactors)) { 
      return(i)
    }
  } 
}

# test the function
vFactors = c(8, 9, 10, 11, 12, 13, 14, 15)
fnDivision(15, vFactors)

Note that I have reduced the number of factors by which I test the division of the sequence of natural numbers from all the natural numbers from 1-15, to the ones above.

Just in case, the correct answer to this is given in A003418, as 360360, and this

all(! 360360 %% vFactors)

evaluates to TRUE.

tchakravarty
  • 10,736
  • 12
  • 72
  • 116
  • @James I would like to answer this question using the the `foreach` package. Indeed [this](http://stackoverflow.com/questions/16079882/is-there-any-way-to-break-out-of-a-foreach-loop) question is closer to the spirit of my question than the one that you have linked. – tchakravarty Oct 11 '13 at 13:21
  • Then you need to ensure you get the correct divisors to divide by. `2^3` is missing from `vFactors`. – James Oct 11 '13 at 13:37
  • 1
    If your question is really about how to use the foreach package, you should flag that by changing the title, which makes it seem like you're asking about how to calculate the least common multiple – Josh O'Brien Oct 11 '13 at 13:43
  • 1
    If you are asking about how to solve [**project euler problems**](http://projecteuler.net/problem=5) then tsk tsk! It's against the spirit of the game! – Simon O'Hanlon Oct 11 '13 at 13:45
  • Would any of those that marked this question as duplicate care to explain? I already answered @James. – tchakravarty Oct 13 '13 at 15:19

4 Answers4

3
help.search("least common multiple") 

library(gmp)
Reduce(lcm.bigz, 1:15)
# Big Integer ('bigz') :
# [1] 360360
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
1

Here you go.

x <- 8:15
p <- prod(x)
min(Reduce(intersect, lapply(x,function(i) seq(i,p,i))))

[1] 360360

And you're probably getting the wrong answer because you're forgetting to include 8.

Thomas
  • 43,637
  • 12
  • 109
  • 140
1

Given your set of reduced divisors, this should be very fast (yes, even though it's a for loop - it only has iterations equal to the length of divisors) and relies on multiplying the greatest power of each of the prime factors in your divisors...

#  For primeFactors function
require( surveillance )
x <- c( 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 )
#  Output vector
out <- numeric(0)
#  Maths magic
for( i in x ){
  tmp <- primeFactors(i)
  out <- c( out , tmp[ ! tmp %in% out ] ) 
}
prod( out )
[1] 360360
Simon O'Hanlon
  • 58,647
  • 14
  • 142
  • 184
1

So after some thought, I figured that my attempt at using the foreach package to access the iterator stream was misguided. Instead, here is the (highly Pythonic) solution that I am happy with:

library(iterators)

# function to check sequence of natural numbers for  
#   divisibility by a given list of factors
fnDivision = function(maxNum, vFactors) {
    i = icount(factorial(15))
    while(TRUE) {
        currentlyTesting = nextElem(i)
            if(all(! currentlyTesting %% vFactors)) { 
            return(currentlyTesting )
            }
    } 
}

# test the function
vFactors = c(8, 9, 10, 11, 12, 13, 14, 15)
sprintf('The smallest natural number divisible by the first 15 natural numbers is %i.', 
    fnDivision(15, vFactors))
tchakravarty
  • 10,736
  • 12
  • 72
  • 116
  • Why don't you test this for speed against the other solutions (especially Josh's - I found his to be twice as fast as mine). I'd be interested to see how they compare. But +1 for answering your own question. – Simon O'Hanlon Oct 12 '13 at 10:13
  • @SimonO101 Unlikely that my code is going to be faster than Josh's given that the `lcm.bigz` probably uses an efficient algorithm to compute the LCM, whereas mine is a brute force approach. – tchakravarty Oct 12 '13 at 14:19
  • Perhaps at least test mine against your parallel solution. The point here is that you can reduce the complexity of the problem by thinking a little about the maths! My `for` loop runs in 0.0002 seconds compared to say 26 seconds for @Thomas' solution on my computer (a factor of 130,000 times faster) :-) – Simon O'Hanlon Oct 12 '13 at 14:23
  • @SimonO101 Well, my code is serial, since I discarded use of `foreach`. Anyway, `system.time(fnDivision(15, vFactors))` takes 7.10s, `system.time(replicate(10000, Reduce(lcm.bigz, 1:15)))` takes 1.22s, essentially a speedup of about 5e4 times. :) – tchakravarty Oct 12 '13 at 14:32
  • @SimonO101 130k times faster! Now I want to see if I can come up with an even slower solution. :) – Thomas Oct 23 '13 at 18:56