How do I optimize this bit of ruby code to go faster? - ruby

It's an implementation of Sieve of Eratosthenes.
class PrimeGenerator
def self.get_primes_between( x, y)
sieve_array = Array.new(y) {|index|
(index == 0 ? 0 : index+1)
}
position_when_we_can_stop_checking = Math.sqrt(y).to_i
(2..position_when_we_can_stop_checking).each{|factor|
sieve_array[(factor).. (y-1)].each{|number|
sieve_array[number-1] = 0 if isMultipleOf(number, factor)
}
}
sieve_array.select{|element|
( (element != 0) && ( (x..y).include? element) )
}
end
def self.isMultipleOf(x, y)
return (x % y) == 0
end
end
Now I did this for a 'submit solutions to problems since you have time to kill' site. I chose ruby as my impl language.. however i was declared timed out.
I did some benchmarking
require 'benchmark'
Benchmark.bmbm do |x|
x.report ("get primes") { PrimeGenerator.get_primes_between(10000, 100000)}
end
ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-mswin32]
L:\Gishu\Ruby>ruby prime_generator.rb
Rehearsal ----------------------------------------------
get primes 33.953000 0.047000 34.000000 ( 34.343750)
------------------------------------ total: 34.000000sec
user system total real
get primes 33.735000 0.000000 33.735000 ( 33.843750)
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
Rehearsal ----------------------------------------------
get primes 65.922000 0.000000 65.922000 ( 66.110000)
------------------------------------ total: 65.922000sec
user system total real
get primes 67.359000 0.016000 67.375000 ( 67.656000)
So I redid the thing in C# 2.0 / VS 2008 -->
722 milliseconds
So now this prods me into thinking is it a problem with my implementation or is the perf diff between languages this wide? (I was amazed with the 1.9 Ruby VM... until I had to go compare it with C# :)
UPDATE:
Turned out to be my "put-eratosthenes-to-shame-adaptation" after all :) Eliminating unnecessary loop iterations was the major optimization. In case anyone is interested in the details.. you can read it here; this question is too long anyways.

I'd start by looking at your inner loop. sieve_array[(factor).. (y-1)] is going to create a new array each time it's executed. Instead, try replacing it with a normal indexing loop.

Obviously each computer is going to benchmark this differently, but I was able to make this run approximately 50x faster on my machine (Ruby 1.8.6) by removing the looping on the array with an each block, and by causing the inner loop to check less numbers.
factor=2
while factor < position_when_we_can_stop_checking
number = factor
while number < y-1
sieve_array[number-1] = 0 if isMultipleOf(number, factor)
number = number + factor; # Was incrementing by 1, causing too many checks
end
factor = factor +1
end

I don't know how it compares for speed, but this is a fairly small and simple SoE implementation that works fine for me:
def sieve_to(n)
s = (0..n).to_a
s[0]=s[1]=nil
s.each do |p|
next unless p
break if p * p > n
(p*p).step(n, p) { |m| s[m] = nil }
end
s.compact
end
There are a few further little speedups possible, but I think it's pretty good.
They're not exactly equivalent, so your 10_000 to 1_000_000 would equate to
sieve_to(1_000_000) - sieve_to(9_999)
or something closely approximate.
Anyhow, on WinXP, with Ruby 1.8.6 (and fairly hefty Xeon CPUs) I get this:
require 'benchmark'
Benchmark.bm(30) do |r|
r.report("Mike") { a = sieve_to(10_000) - sieve_to(1_000) }
r.report("Gishu") { a = PrimeGenerator.get_primes_between( 1_000, 10_000) }
end
which gives
user system total real
Mike 0.016000 0.000000 0.016000 ( 0.016000)
Gishu 1.641000 0.000000 1.641000 ( 1.672000)
(I stopped running the one million case because I got bored waiting).
So I'd say it was your algorithm. ;-)
The C# solution is pretty much guaranteed to be orders of magnitude faster though.

The Sieve of Eratosthenes works fine as illustrative way to find primes, but I would implement it a little bit different. The essence is that you don't have to check numbers which are multiples of already known primes. Now, instead of using an array to store this information, you can also create a list of all sequential primes up to the square root of the number you are checking, and then it suffices to go through the list of primes to check for primes.
If you think of it, this does what you do on the image, but in a more "virtual" way.
Edit: Quickly hacked implementation of what I mean (not copied from the web ;) ):
public class Sieve {
private readonly List<int> primes = new List<int>();
private int maxProcessed;
public Sieve() {
primes.Add(maxProcessed = 2); // one could add more to speed things up a little, but one is required
}
public bool IsPrime(int i) {
// first check if we can compare against known primes
if (i <= primes[primes.Count-1]) {
return primes.BinarySearch(i) >= 0;
}
// if not, make sure that we got all primes up to the square of i
int maxFactor = (int)Math.Sqrt(i);
while (maxProcessed < maxFactor) {
maxProcessed++;
bool isPrime = true;
for (int primeIndex = 0; primeIndex < primes.Count; primeIndex++) {
int prime = primes[primeIndex];
if (maxProcessed % prime == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.Add(maxProcessed);
}
}
// now apply the sieve to the number to check
foreach (int prime in primes) {
if (i % prime == 0) {
return false;
}
if (prime > maxFactor) {
break;
}
}
return true;
}
}
Uses about 67ms on my slow machine.... test app:
class Program {
static void Main(string[] args) {
Stopwatch sw = new Stopwatch();
sw.Start();
Sieve sieve = new Sieve();
for (int i = 10000; i <= 100000; i++) {
sieve.IsPrime(i);
}
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds);
}
}

Benchmark it with ruby-prof. it can spit out things tools like kcachegrind can look at to see where your code is slow.
Then once you make the ruby fast, use RubyInline to optimize the method for you.

I would also note that Ruby, in my experience, is a lot slower on Windows systems than on *nix. I'm not sure what speed processor you have, of course, but running this code on my Ubuntu box in Ruby 1.9 took around 10 seconds, and 1.8.6 took 30.

Related

Prime number performance difference ACF vs. Lucee

The following code to find prime numbers greatly differs between Adobe ColdFusion (10) and Lucee (4.5) regarding performance. Tested on the same machine (6C i7 3930k # 4 GHz, Windows 10 64 Bit, JVM memory settings equal on both CFML engines: JDK7 -Xms512m -Xmx2048m -XX:MaxPermSize=512m):
<cfscript>
ticks = getTickCount();
stopIndex = 10000;
primes = [];
divisions = 0;
primes.add(2);
primes.add(3);
n = 5;
for (n; n < stopIndex; n += 2) {
isPrime = true;
d = 3;
for (d; d < n; d++) {
divisions++;
if (n % d == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.add(n);
}
}
ticks = (getTickCount() - ticks);
</cfscript>
<cfoutput>
<p>
#numberFormat(divisions)# divisions in #ticks# ms.
</p>
<p>
#numberFormat(arrayLen(primes))# prime numbers found below #numberFormat(stopIndex)#.
</p>
</cfoutput>
stopIndex # 10k
ACF: 280 ms
LUC: 1300 ms
stopIndex # 20k
ACF: 1000 ms
LUC: 4800 ms
stopIndex # 30k
ACF: 2200 ms
LUC: 10500 ms
trycf.com and cflive.net show a similar gap.
I checked if cfscript (vs. tags) has impact on the time, it doesn't. CFML engine related server settings do not seem to have any noticable impact either.
What could be the reason for the performance difference?
And how could I possibly resolve this?
Background: I'm running heavy math ops (geometry, image rendering)) on a production server, which happens to be running Lucee, and noticed the sluggish performance.
Integer maths is slower than floating point maths, so this runs slower because of the way that Lucee stores variables as types. if you force the n to be non integer Lucee runs 4x faster
if ((n+1.0) % d == 0) {
This tweak also more than doubles the speed in ACF too
https://luceeserver.atlassian.net/browse/LDEV-1541
I think the performance issue side of things is down to Lucee to fix, rather than you and your code.
However from the perspective of overall performance of this particular algorithm, they best economy you can make is to loop to sqr(n)+1 rather than all the way to n. You're doing way more work than you need to be, and that's a bigger contributor to the performance of this code than the platform differences.
Also you only need to loop over the preceding primes, not every (second) number. That'd improve your performance further.
I realise the algorithm is just an example, but in all honesty the rest of it isn't something yer likely to be able to fix. Raise a ticket with Lucee and wait for it to be fixed (or DIY, if you have the time / Java knowledge).
I know this is not answering the question, but taking Adam's comment further, it doesn't even have to be up to the square root. Once you realize a number isn't divisible by 3, you can limit your top to the results of the division by 3. Tracking previous prime numbers takes memory when your n becomes large. If you can afford that, that's great. If you can't, then incrementing the denominator by 2 would speed it up by 2 since you're skipping even numbers. This code is about 50 times faster:
<cfscript>
ticks = getTickCount();
stopIndex = 10000;
primes = [];
divisions = 0;
primes.add(2);
primes.add(3);
n = 5;
for (n; n < stopIndex; n += 2) {
isPrime = true;
d=3;
n2=n;
for (d; d < n2; ) {
divisions++;
Result=n/d;
if (Result eq Int(Result)) {
isPrime = false;
break;
} else {
d+=2;
n2=Result;
}
}
if (isPrime) {
primes.add(n);
}
}
ticks = (getTickCount() - ticks);
</cfscript>
56,570 divisions in 32 ms (vs. 1500 before on my machine)
1,229 prime numbers found below 10,000.

Why is array.min so slow?

I noticed that array.min seems slow, so I did this test against my own naive implementation:
require 'benchmark'
array = (1..100000).to_a.shuffle
Benchmark.bmbm(5) do |x|
x.report("lib:") { 99.times { min = array.min } }
x.report("own:") { 99.times { min = array[0]; array.each { |n| min = n if n < min } } }
end
The results:
Rehearsal -----------------------------------------
lib: 1.531000 0.000000 1.531000 ( 1.538159)
own: 1.094000 0.016000 1.110000 ( 1.102130)
-------------------------------- total: 2.641000sec
user system total real
lib: 1.500000 0.000000 1.500000 ( 1.515249)
own: 1.125000 0.000000 1.125000 ( 1.145894)
I'm shocked. How can my own implementation running a block via each beat the built-in? And beat it by so much?
Am I somehow mistaken? Or is this somehow normal? I'm confused.
My Ruby version, running on Windows 8.1 Pro:
C:\>ruby --version
ruby 2.2.3p173 (2015-08-18 revision 51636) [i386-mingw32]
Have a look at the implementation of Enumerable#min. It might use each eventually to loop through the elements and get the min element, but before that it does some extra checking to see if it needs to return more than one element, or if it needs to compare the elements via a passed block. In your case the elements will get to be compared via min_i function, and I suspect that's where the speed difference comes from - that function will be slower than simply comparing two numbers.
There's no extra optimization for arrays, all enumerables are traversed the same way.
It's even faster if you use:
def my_min(ary)
the_min = ary[0]
i = 1
len = ary.length
while i < len
the_min = ary[i] if ary[i] < the_min
i += 1
end
the_min
end
NOTE
I know this is not an answer, but I thought it was worth sharing and putting this code into a comment would have been exceedingly ugly.
For those who likes to upgrade to newer versions of software
require 'benchmark'
array = (1..100000).to_a.shuffle
Benchmark.bmbm(5) do |x|
x.report("lib:") { 99.times { min = array.min } }
x.report("own:") { 99.times { min = array[0]; array.each { |n| min = n if n < min } } }
end
Rehearsal -----------------------------------------
lib: 0.021326 0.000017 0.021343 ( 0.021343)
own: 0.498233 0.001024 0.499257 ( 0.499746)
-------------------------------- total: 0.520600sec
user system total real
lib: 0.018126 0.000000 0.018126 ( 0.018139)
own: 0.492046 0.000000 0.492046 ( 0.492367)
RUBY_VERSION # => "2.7.1"
If you are looking into solving this in really performant manner: O(log(n)) or O(n), look at https://en.wikipedia.org/wiki/Selection_algorithm#Incremental_sorting_by_selection and https://en.wikipedia.org/wiki/Heap_(data_structure)

Trial division for primes with immutable collections in Scala

I am trying to learn Scala and functional programming ideology by rewriting basic exercises. Currently I have trouble with naive approach for generating primes "trial division".
The trouble described below is that I could not rewrite well-known algorithm in functional style preserving efficiency, because I have no suitable immutable data structure, like a List but with fast operations not only on head, but also on the very end.
I started with writing java code which for every odd number tests its divisibility by already found primes (limited by square root of value being tested) - and adds it to the end of the list if no divisor was found.
http://ideone.com/QE8U0I
List<Integer> primes = new ArrayList<>();
primes.add(2);
int cur = 3;
while (primes.size() < 100000) {
for (Integer x : primes) {
if (x * x > cur) {
primes.add(cur);
break;
}
if (cur % x == 0) {
break;
}
}
cur += 2;
}
Now I tried to rewrite it in "functional way" - there was no problem with using recursion instead of loops, but I stuck with immutable collections. Core idea is as following:
http://ideone.com/4DQ6mi
def primes(n: Int) = {
#tailrec
def divisibleByAny(x: Int, list: List[Int]): Boolean = {
if (list.isEmpty) false else {
val h = list.head
h * h <= x && (x % h == 0 || divisibleByAny(x, list.tail))
}
}
#tailrec
def morePrimes(from: Int, prev: List[Int]): List[Int] = {
if (prev.size == n) prev else
morePrimes(from + 2, if (divisibleByAny(from, prev)) prev else prev :+ from)
}
morePrimes(3, List(2))
}
But it is slow - if I understand correctly because operation of adding to the end of immutable list requires creation of new copy of the whole stuff.
I searched over documentation to find more suitable data structure and tried to substitute list with immutable Queue, for it is said:
Adding items to the queue always has cost O(1) ... Removing an item is on average O(1).
But it is still even slower:
http://ideone.com/v8BsuQ
def primes(n: Int) = {
#tailrec
def divisibleByAny(x: Int, list: Queue[Int]): Boolean = {
if (list.isEmpty) false else {
val (h, t) = list.dequeue
h * h <= x && (x % h == 0 || divisibleByAny(x, t))
}
}
#tailrec
def morePrimes(from: Int, prev: Queue[Int]): Queue[Int] = {
if (prev.size == n) prev else
morePrimes(from + 2, if (divisibleByAny(from, prev)) prev else prev.enqueue(from))
}
morePrimes(3, Queue(2))
}
What is going wrong or am I missing something?
P.S. I believe there are other algorithms for generating primes which are more suitable for functional style. I think I've seen some paper. But now I'm interested in this one, or more precisely in existence of suitable data structure.
According to http://docs.scala-lang.org/overviews/collections/performance-characteristics.html Vectors have an amortised constant cost for appending, prepending and seeking. Indeed, using vectors instead of lists in your solution is much faster
def primes(n: Int) = {
#tailrec
def divisibleByAny(x: Int, list: Vector[Int]): Boolean = {
if (list.isEmpty) false else {
val (h +: t) = list
h * h <= x && (x % h == 0 || divisibleByAny(x, t))
}
}
#tailrec
def morePrimes(from: Int, prev: Vector[Int]): Vector[Int] = {
if (prev.length == n) prev else
morePrimes(from + 2, if (divisibleByAny(from, prev)) prev else prev :+ from)
}
morePrimes(3, Vector(2))
}
http://ideone.com/x3k4A3
I think you have 2 main options
Use a Vector - which is better than a list for appending. It is a Bitmapped Trie data structure (http://en.wikipedia.org/wiki/Trie). It’s “effectively” O(1) for appending to (i.e. O(1) on average)
Or...possibly the answer you're not looking for
Use a mutable data structure like ListBuffer - immutability it great to try achieve, and should be your go to collections - but sometimes for efficiency reasons, you may use mutable structures . What is key it to make sure it does not “leak out” of your classes. If you look at the List.scala implementation, you’ll see ListBuffer used a lot internally. However, its coverted back to a List just before it leaves the class. If its good enough for the core Scala libraries, its probably ok for you to use under exceptional cases that warrant it.
Except using Vector, also consider using higher-order functions instead of recursion. That's also a completely valid functional style. On my machine the following implementation of divisibleByAny is about 8x faster, than #Pyetras tailrec implementation when running primes(1000000):
def divisibleByAny(x: Int, list: Vector[Int]): Boolean =
list.view.takeWhile(el => el * el <= x).exists(x % _ == 0)

Find prime numbers using Scala. Help me to improve

I wrote this code to find the prime numbers less than the given number i in scala.
def findPrime(i : Int) : List[Int] = i match {
case 2 => List(2)
case _ => {
val primeList = findPrime(i-1)
if(isPrime(i, primeList)) i :: primeList else primeList
}
}
def isPrime(num : Int, prePrimes : List[Int]) : Boolean = prePrimes.forall(num % _ != 0)
But, I got a feeling the findPrime function, especially this part:
case _ => {
val primeList = findPrime(i-1)
if(isPrime(i, primeList)) i :: primeList else primeList
}
is not quite in the functional style.
I am still learning functional programming. Can anyone please help me improve this code to make it more functional.
Many thanks.
Here's a functional implementation of the Sieve of Eratosthenes, as presented in Odersky's "Functional Programming Principles in Scala" Coursera course :
// Sieving integral numbers
def sieve(s: Stream[Int]): Stream[Int] = {
s.head #:: sieve(s.tail.filter(_ % s.head != 0))
}
// All primes as a lazy sequence
val primes = sieve(Stream.from(2))
// Dumping the first five primes
print(primes.take(5).toList) // List(2, 3, 5, 7, 11)
The style looks fine to me. Although the Sieve of Eratosthenes is a very efficient way to find prime numbers, your approach works well too, since you are only testing for division against known primes. You need to watch out however--your recursive function is not tail recursive. A tail recursive function does not modify the result of the recursive call--in your example you prepend to the result of the recursive call. This means that you will have a long call stack and so findPrime will not work for large i. Here is a tail-recursive solution.
def primesUnder(n: Int): List[Int] = {
require(n >= 2)
def rec(i: Int, primes: List[Int]): List[Int] = {
if (i >= n) primes
else if (prime(i, primes)) rec(i + 1, i :: primes)
else rec(i + 1, primes)
}
rec(2, List()).reverse
}
def prime(num: Int, factors: List[Int]): Boolean = factors.forall(num % _ != 0)
This solution isn't prettier--it's more of a detail to get your solution to work for large arguments. Since the list is built up backwards to take advantage of fast prepends, the list needs to be reversed. As an alternative, you could use an Array, Vector or a ListBuffer to append the results. With the Array, however, you would need to estimate how much memory to allocate for it. Fortunately we know that pi(n) is about equal to n / ln(n) so you can choose a reasonable size. Array and ListBuffer are also a mutable data types, which goes again your desire for functional style.
Update: to get good performance out of the Sieve of Eratosthenes I think you'll need to store data in a native array, which also goes against your desire for style in functional programming. There might be a creative functional implementation though!
Update: oops! Missed it! This approach works well too if you only divide by primes less than the square root of the number you are testing! I missed this, and unfortunately it's not easy to adjust my solution to do this because I'm storing the primes backwards.
Update: here's a very non-functional solution that at least only checks up to the square root.
rnative, you could use an Array, Vector or a ListBuffer to append the results. With the Array, however, you would need to estimate how much memory to allocate for it. Fortunately we know that pi(n) is about equal to n / ln(n) so you can choose a reasonable size. Array and ListBuffer are also a mutable data types, which goes again your desire for functional style.
Update: to get good performance out of the Sieve of Eratosthenes I think you'll need to store data in a native array, which also goes against your desire for style in functional programming. There might be a creative functional implementation though!
Update: oops! Missed it! This approach works well too if you only divide by primes less than the square root of the number you are testing! I missed this, and unfortunately it's not easy to adjust my solution to do this because I'm storing the primes backwards.
Update: here's a very non-functional solution that at least only checks up to the square root.
import scala.collection.mutable.ListBuffer
def primesUnder(n: Int): List[Int] = {
require(n >= 2)
val primes = ListBuffer(2)
for (i <- 3 to n) {
if (prime(i, primes.iterator)) {
primes += i
}
}
primes.toList
}
// factors must be in sorted order
def prime(num: Int, factors: Iterator[Int]): Boolean =
factors.takeWhile(_ <= math.sqrt(num).toInt) forall(num % _ != 0)
Or I could use Vectors with my original approach. Vectors are probably not the best solution because they don't have the fasted O(1) even though it's amortized O(1).
As schmmd mentions, you want it to be tail recursive, and you also want it to be lazy. Fortunately there is a perfect data-structure for this: Stream.
This is a very efficient prime calculator implemented as a Stream, with a few optimisations:
object Prime {
def is(i: Long): Boolean =
if (i == 2) true
else if ((i & 1) == 0) false // efficient div by 2
else prime(i)
def primes: Stream[Long] = 2 #:: prime3
private val prime3: Stream[Long] = {
#annotation.tailrec
def nextPrime(i: Long): Long =
if (prime(i)) i else nextPrime(i + 2) // tail
def next(i: Long): Stream[Long] =
i #:: next(nextPrime(i + 2))
3 #:: next(5)
}
// assumes not even, check evenness before calling - perf note: must pass partially applied >= method
def prime(i: Long): Boolean =
prime3 takeWhile (math.sqrt(i).>= _) forall { i % _ != 0 }
}
Prime.is is the prime check predicate, and Prime.primes returns a Stream of all prime numbers. prime3 is where the Stream is computed, using the prime predicate to check for all prime divisors less than the square root of i.
/**
* #return Bitset p such that p(x) is true iff x is prime
*/
def sieveOfEratosthenes(n: Int) = {
val isPrime = mutable.BitSet(2 to n: _*)
for (p <- 2 to Math.sqrt(n) if isPrime(p)) {
isPrime --= p*p to n by p
}
isPrime.toImmutable
}
A sieve method is your best bet for small lists of numbers (up to 10-100 million or so).
see: Sieve of Eratosthenes
Even if you want to find much larger numbers, you can use the list you generate with this method as divisors for testing numbers up to n^2, where n is the limit of your list.
#mfa has mentioned using a Sieve of Eratosthenes - SoE and #Luigi Plinge has mentioned that this should be done using functional code, so #netzwerg has posted a non-SoE version; here, I post a "almost" functional version of the SoE using completely immutable state except for the contents of a mutable BitSet (mutable rather than immutable for performance) that I posted as an answer to another question:
object SoE {
def makeSoE_Primes(top: Int): Iterator[Int] = {
val topndx = (top - 3) / 2
val nonprms = new scala.collection.mutable.BitSet(topndx + 1)
def cullp(i: Int) = {
import scala.annotation.tailrec; val p = i + i + 3
#tailrec def cull(c: Int): Unit = if (c <= topndx) { nonprms += c; cull(c + p) }
cull((p * p - 3) >>> 1)
}
(0 to (Math.sqrt(top).toInt - 3) >>> 1).filterNot { nonprms }.foreach { cullp }
Iterator.single(2) ++ (0 to topndx).filterNot { nonprms }.map { i: Int => i + i + 3 }
}
}
How about this.
def getPrimeUnder(n: Int) = {
require(n >= 2)
val ol = 3 to n by 2 toList // oddList
def pn(ol: List[Int], pl: List[Int]): List[Int] = ol match {
case Nil => pl
case _ if pl.exists(ol.head % _ == 0) => pn(ol.tail, pl)
case _ => pn(ol.tail, ol.head :: pl)
}
pn(ol, List(2)).reverse
}
It's pretty fast for me, in my mac, to get all prime under 100k, its take around 2.5 sec.
A scalar fp approach
// returns the list of primes below `number`
def primes(number: Int): List[Int] = {
number match {
case a
if (a <= 3) => (1 to a).toList
case x => (1 to x - 1).filter(b => isPrime(b)).toList
}
}
// checks if a number is prime
def isPrime(number: Int): Boolean = {
number match {
case 1 => true
case x => Nil == {
2 to math.sqrt(number).toInt filter(y => x % y == 0)
}
}
}
def primeNumber(range: Int): Unit ={
val primeNumbers: immutable.IndexedSeq[AnyVal] =
for (number :Int <- 2 to range) yield {
val isPrime = !Range(2, Math.sqrt(number).toInt).exists(x => number % x == 0)
if(isPrime) number
}
for(prime <- primeNumbers) println(prime)
}
object Primes {
private lazy val notDivisibleBy2: Stream[Long] = 3L #:: notDivisibleBy2.map(_ + 2)
private lazy val notDivisibleBy2Or3: Stream[Long] = notDivisibleBy2
.grouped(3)
.map(_.slice(1, 3))
.flatten
.toStream
private lazy val notDivisibleBy2Or3Or5: Stream[Long] = notDivisibleBy2Or3
.grouped(10)
.map { g =>
g.slice(1, 7) ++ g.slice(8, 10)
}
.flatten
.toStream
lazy val primes: Stream[Long] = 2L #::
notDivisibleBy2.head #::
notDivisibleBy2Or3.head #::
notDivisibleBy2Or3Or5.filter { i =>
i < 49 || primes.takeWhile(_ <= Math.sqrt(i).toLong).forall(i % _ != 0)
}
def apply(n: Long): Stream[Long] = primes.takeWhile(_ <= n)
def getPrimeUnder(n: Long): Long = Primes(n).last
}

Airplane scheduling challenge from 2009 ACM-ICPC World Finals

Out of curiosity, I was checking out the problem set to the 2009 ACM International Collegiate Programming Contest. The questions are pretty interesting. They're available at http://cm.baylor.edu/resources/pdf/2009Problems.pdf. I could not come up with an algorithm that solved problem 1, which I will reproduce here. It set off a lively discussion in the office, and we think we're pretty close to an answer, but we'd really appreciate it if somebody could find/work out a full solution (code not required).
I will reproduce problem here for your convenience:
Problem 1
Consider the task of scheduling the airplanes that are landing at an airport. Incoming airplanes report their positions, directions, and speeds, and then the controller has to devise a landing schedule that brings all airplanes safely to the ground. Generally, the more time there is between successive landings, the “safer” a landing schedule is. This extra time gives pilots the opportunity to react to changing weather and other surprises.
Luckily, part of this scheduling task can be automated – this is where you come in. You will be given scenarios of airplane landings. Each airplane has a time window during which it can safely land. You must compute an order for landing all airplanes that respects these time windows. Furthermore, the airplane landings should be stretched out as much as possible so that the minimum time gap between successive landings is as large as possible. For example, if three airplanes land at 10:00am, 10:05am, and 10:15am, then the smallest gap is five minutes, which occurs between the first two airplanes. Not all gaps have to be the same, but the smallest gap should be as large as possible.
Input
The input file contains several test cases consisting of descriptions of landing scenarios. Each test case starts with a line containing a single integer n (2 ≤ n ≤ 8), which is the number of airplanes in the scenario. This is followed by n lines, each containing two integers ai, bi, which give the beginning and end of the closed interval [ai, bi] during which the ith plane can land safely. The numbers ai and bi are specified in minutes and satisfy 0 ≤ ai ≤ bi ≤ 1440.
The input is terminated with a line containing the single integer zero.
Output
For each test case in the input, print its case number (starting with 1) followed by the minimum achievable time gap between successive landings. Print the time split into minutes and seconds, rounded to the closest second. Follow the format of the sample output.
Sample Input
3
0 10
5 15
10 15
2
0 10
10 20
0
Sample Output
Case 1: 7:30
Case 2: 20:00
I'll give a sketch of the algorithm.
First you binary search through the answer (minimal interval between flights). To do that, for each selected interval T you must be able to check whether it is possible to achieve it. If it is possible to achieve T, then you try making it smaller, if it is not - make it bigger.
To check whether you can achieve T, try all n! orders in which the planes may be landing (8! is small enough for this algo to work in time). For each permutation P1...Pn, you try assigning the times in a greedy manner:
int land = a[0];
for (int i = 1; i < n; i++) {
land = max(a[i], land + **T**);
if (land > b[i]) return "CAN NOT ACHIEVE INTERVAL T";
}
return "CAN ACHIEVE";
This optimization problem can be solved by linear programming http://en.wikipedia.org/wiki/Linear_programming
I would do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint MASK;
#define INPUT_SCALE 60
#define MAX_TIME (1440 * 60)
void readPlaneData(int& endTime, MASK landingMask[MAX_TIME], int index)
{
char buf[128];
gets(buf);
int start, end;
sscanf(buf, "%d %d", &start, &end);
for(int i=start * INPUT_SCALE; i<=end * INPUT_SCALE; i++)
landingMask[i] |= 1 << index;
if(end * INPUT_SCALE > endTime)
endTime = end * INPUT_SCALE;
}
int findNextLandingForPlane(MASK landingMask[MAX_TIME], int start, int index)
{
while(start < MAX_TIME)
{
if(landingMask[start] & (1 << index))
return start;
start++;
}
return -1;
}
bool canLandPlanes(int minTime, MASK landingMask[MAX_TIME], int planeCount)
{
int next = 0;
for(int i=0; i<planeCount; i++)
{
int nextForPlane = findNextLandingForPlane(landingMask, next, i);
if(nextForPlane == -1)
return false;
next = nextForPlane + minTime;
}
return true;
}
int main(int argc, char* argv[])
{
while(true)
{
char buf[128];
gets(buf);
int count = atoi(buf);
if(count == 0)
break;
MASK landingMask[MAX_TIME];
memset(landingMask, 0, sizeof(landingMask));
int endTime = 0;
for(int i=0; i<count; i++)
readPlaneData(endTime, landingMask, i);
while((endTime > 0) && !canLandPlanes(endTime, landingMask, count))
endTime--;
printf("%d:%02d\n", endTime / 60, endTime % 60);
}
}
Here's some Ruby code that brute-forces the solution. Note that test_case_one actually fails because I have commented out the code that would make this work with seconds (instead of just whole minutes).
The brute-force strategy is to permute all the sequences in which the planes may land. For each landing sequence, create the product of all possible landing times. This is fine with whole minutes, brutal with seconds.
But of course premature optimization, evil, and all that, so this is a first step:
require 'test/unit'
class SampleTests < Test::Unit::TestCase
def test_case_one
problem = Problem.new
problem.add_plane(Plane.new(0, 10))
problem.add_plane(Plane.new(5, 15))
problem.add_plane(Plane.new(10, 15))
problem.solve()
minimum_gap = problem.minimum_gap()
assert_equal(7.5, minimum_gap)
end
def test_case_two
problem = Problem.new
problem.add_plane(Plane.new(0,10))
problem.add_plane(Plane.new(10, 20))
problem.solve()
minimum_gap = problem.minimum_gap()
assert_equal(20, minimum_gap)
end
def test_case_three
problem = Problem.new
problem.add_plane(Plane.new(0, 2))
problem.add_plane(Plane.new(7, 10))
problem.add_plane(Plane.new(4, 6))
minimum_gap = problem.minimum_gap()
assert_equal(5, minimum_gap)
end
def test_case_four
problem = Problem.new
problem.add_plane(Plane.new(1439, 1440))
problem.add_plane(Plane.new(1439, 1440))
problem.add_plane(Plane.new(1439, 1440))
assert_equal(0, problem.minimum_gap())
end
def test_case_five
problem = Problem.new
problem.add_plane(Plane.new(0, 10))
problem.add_plane(Plane.new(1, 2))
assert_equal(9, problem.minimum_gap())
end
def test_case_six
problem = Problem.new
problem.add_plane(Plane.new(8, 9))
problem.add_plane(Plane.new(0, 10))
assert_equal(9, problem.minimum_gap())
end
end
class Plane
def initialize(min, max)
#ts = Array.new
#This is a cheat to prevent combinatorial explosion. Just ignore 60 seconds in a minute!
#min = min * 60
#max = max * 60
min.upto(max) { | t | #ts << t}
end
#Array of times at which the plane might land.
def times
return #ts
end
end
#from 'permutation' gem
class Array
def permute(prefixed=[])
if (length < 2)
# there are no elements left to permute
yield(prefixed + self)
else
# recursively permute the remaining elements
each_with_index do |e, i|
(self[0,i]+self[(i+1)..-1]).permute(prefixed+[e]) { |a| yield a }
end
end
end
end
class Problem
def initialize
#solved = false
#maximum_gap = 0
#planes = Array.new
end
def add_plane(plane)
#planes << plane
end
#given a particular landing schedule, what's the minimum gap?
#A: Sort schedule and spin through it, looking for the min diff
#Note that this will return 0 for invalid schedules (planes landing simultaneously)
def gap_for(schedule)
schedule.sort!
min_gap = 1440
0.upto(schedule.length - 2) { | i |
gap = schedule[i + 1] - schedule[i]
if gap < min_gap
min_gap = gap
end
}
return min_gap
end
#Brute-force strategy
#Get every possible plane sequence (permute)
#Get every possible schedule for that sequence (brute_force_schedule)
#Check that schedule
def solve
#planes.permute { | sequence |
schedules = brute_force_schedule(sequence)
schedules.each { | schedule |
schedule.flatten!
gap = gap_for(schedule)
if gap > #maximum_gap
#puts "Found a new one: #{schedule.inspect}"
#maximum_gap = gap
end
}
}
end
#The list of all possible schedules associated with an array of planes
def brute_force_schedule(planes)
head = planes[0]
tail = planes[1..-1]
if tail.empty?
#Last element, return the times
return head.times.to_a
else
#Recurse and combine (product)
return head.times.to_a.product(brute_force_schedule(tail))
end
end
def minimum_gap
unless #solved
solve
end
return #maximum_gap
end
end

Resources