Scala how to define an ordering for Rationals - algorithm

I have to implement compareRationals as something like
(a, b) => {
the body goes here
}
to compare to fractions, transform them so they both have the same denominator, then order the two results by their numerator to make sure they have the same denominator, need to find out the Least Common Denominator so my code works for println(insertionSort2(List(rationals))) and currently works for all the println statements besides that. I really need help to define compareRationals so println(insertionSort2(List(rationals))) shouldBe List(fourth, third, half)
Object {
def insertionSort2[A](xs: List[A])(implicit ord: Ordering[A]): List[A] = {
def insert2(y: A, ys: List[A]): List[A] =
ys match {
case List() => y :: List()
case z :: zs =>
if (ord.lt(y, z)) y :: z :: zs
else z :: insert2(y, zs)
}
xs match {
case List() => List()
case y :: ys => insert2(y, insertionSort2(ys))
}
}
class Rational(x: Int, y: Int) {
private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
private val g = gcd(x, y)
lazy val numer: Int = x / g
lazy val denom: Int = y / g
}
val compareRationals: (Rational, Rational) => Int =
implicit val rationalOrder: Ordering[Rational] =
new Ordering[Rational] {
def compare(x: Rational, y: Rational): Int = compareRationals(x, y)
}
def main(args: Array[String]): Unit = {
val half = new Rational(1, 2)
val third = new Rational(1, 3)
val fourth = new Rational(1, 4)
val rationals = List(third, half, fourth)
println(insertionSort2(List(4,2,9,5,8))(Ordering.Int))
println(insertionSort2(List(4,2,9,5,8)))
println(insertionSort2(List(rationals)))
}
}
}

I think this is all you need.
val compareRationals: (Rational, Rational) => Int =
(x,y) => x.numer * y.denom - y.numer * x.denom

Related

Tail recursive solution in Scala for Linked-List chaining

I wanted to write a tail-recursive solution for the following problem on Leetcode -
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contains a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:
*Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)*
*Output: 7 -> 0 -> 8*
*Explanation: 342 + 465 = 807.*
Link to the problem on Leetcode
I was not able to figure out a way to call the recursive function in the last line.
What I am trying to achieve here is the recursive calling of the add function that adds the heads of the two lists with a carry and returns a node. The returned node is chained with the node in the calling stack.
I am pretty new to scala, I am guessing I may have missed some useful constructs.
/**
* Definition for singly-linked list.
* class ListNode(_x: Int = 0, _next: ListNode = null) {
* var next: ListNode = _next
* var x: Int = _x
* }
*/
import scala.annotation.tailrec
object Solution {
def addTwoNumbers(l1: ListNode, l2: ListNode): ListNode = {
add(l1, l2, 0)
}
//#tailrec
def add(l1: ListNode, l2: ListNode, carry: Int): ListNode = {
var sum = 0;
sum = (if(l1!=null) l1.x else 0) + (if(l2!=null) l2.x else 0) + carry;
if(l1 != null || l2 != null || sum > 0)
ListNode(sum%10,add(if(l1!=null) l1.next else null, if(l2!=null) l2.next else null,sum/10))
else null;
}
}
You have a couple of problems, which can mostly be reduced as being not idiomatic.
Things like var and null are not common in Scala and usually, you would use a tail-recursive algorithm to avoid that kind of things.
Finally, remember that a tail-recursive algorithm requires that the last expression is either a plain value or a recursive call. For doing that, you usually keep track of the remaining job as well as an accumulator.
Here is a possible solution:
type Digit = Int // Refined [0..9]
type Number = List[Digit] // Refined NonEmpty.
def sum(n1: Number, n2: Number): Number = {
def aux(d1: Digit, d2: Digit, carry: Digit): (Digit, Digit) = {
val tmp = d1 + d2 + carry
val d = tmp % 10
val c = tmp / 10
d -> c
}
#annotation.tailrec
def loop(r1: Number, r2: Number, acc: Number, carry: Digit): Number =
(r1, r2) match {
case (d1 :: tail1, d2 :: tail2) =>
val (d, c) = aux(d1, d2, carry)
loop(r1 = tail1, r2 = tail2, d :: acc, carry = c)
case (Nil, d2 :: tail2) =>
val (d, c) = aux(d1 = 0, d2, carry)
loop(r1 = Nil, r2 = tail2, d :: acc, carry = c)
case (d1 :: tail1, Nil) =>
val (d, c) = aux(d1, d2 = 0, carry)
loop(r1 = tail1, r2 = Nil, d :: acc, carry = c)
case (Nil, Nil) =>
acc
}
loop(r1 = n1, r2 = n2, acc = List.empty, carry = 0).reverse
}
Now, this kind of recursions tends to be very verbose.
Usually, the stdlib provide ways to make this same algorithm more concise:
// This is a solution that do not require the numbers to be already reversed and the output is also in the correct order.
def sum(n1: Number, n2: Number): Number = {
val (result, carry) = n1.reverseIterator.zipAll(n2.reverseIterator, 0, 0).foldLeft(List.empty[Digit] -> 0) {
case ((acc, carry), (d1, d2)) =>
val tmp = d1 + d2 + carry
val d = tmp % 10
val c = tmp / 10
(d :: acc) -> c
}
if (carry > 0) carry :: result else result
}
Scala is less popular on LeetCode, but this Solution (which is not the best) would get accepted by LeetCode's online judge:
import scala.collection.mutable._
object Solution {
def addTwoNumbers(listA: ListNode, listB: ListNode): ListNode = {
var tempBufferA: ListBuffer[Int] = ListBuffer.empty
var tempBufferB: ListBuffer[Int] = ListBuffer.empty
tempBufferA.clear()
tempBufferB.clear()
def listTraversalA(listA: ListNode): ListBuffer[Int] = {
if (listA == null) {
return tempBufferA
} else {
tempBufferA += listA.x
listTraversalA(listA.next)
}
}
def listTraversalB(listB: ListNode): ListBuffer[Int] = {
if (listB == null) {
return tempBufferB
} else {
tempBufferB += listB.x
listTraversalB(listB.next)
}
}
val resultA: ListBuffer[Int] = listTraversalA(listA)
val resultB: ListBuffer[Int] = listTraversalB(listB)
val resultSum: BigInt = BigInt(resultA.reverse.mkString) + BigInt(resultB.reverse.mkString)
var listNodeResult: ListBuffer[ListNode] = ListBuffer.empty
val resultList = resultSum.toString.toList
var lastListNode: ListNode = null
for (i <-0 until resultList.size) {
if (i == 0) {
lastListNode = new ListNode(resultList(i).toString.toInt)
listNodeResult += lastListNode
} else {
lastListNode = new ListNode(resultList(i).toString.toInt, lastListNode)
listNodeResult += lastListNode
}
}
return listNodeResult.reverse(0)
}
}
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions, explanations, efficient algorithms with a variety of languages, and time/space complexity analysis in there.

How to get a termination reason from a recursive function?

Suppose a function is looped to produce a numeric result. The looping is stopped either if the iterations maximum is reached or the "optimality" condition is met. In either case, the value from the current loop is output. What is a functional way to get both this result and the stopping reason?
For illustration, here's my Scala implementation of the "Square Roots" example in 4.1 of https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf.
object SquareRootAlg {
def next(a: Double)(x: Double): Double = (x + a/x)/2
def repeat[A](f: A=>A, a: A): Stream[A] = a #:: repeat(f, f(a))
def loopConditional[A](stop: (A, A) => Boolean)(s: => Stream[A] ): A = s match {
case a #:: t if t.isEmpty => a
case a #:: t => if (stop(a, t.head)) t.head else loopConditional(stop)(t)}
}
Eg, to find the square root of 4:
import SquareRootAlg._
val cond = (a: Double, b: Double) => (a-b).abs < 0.01
val alg = loopConditional(cond) _
val s = repeat(next(4.0), 4.0)
alg(s.take(3)) // = 2.05, "maxIters exceeded"
alg(s.take(5)) // = 2.00000009, "optimality reached"
This code works, but doesn't give me the stopping reason. So
I'm trying to write a method
def loopConditionalInfo[A](stop: (A, A)=> Boolean)(s: => Stream[A]): (A, Boolean)
outputting (2.05, false) in the first case above, and (2.00000009, true) in the second. Is there a way to write this method without modifying the next and repeat methods? Or would another functional approach work better?
Typically, you need to return a value that includes both a stopping reason and the result. Using the (A, Boolean) return signature you propose allows for this.
Your code would then become:
import scala.annotation.tailrec
object SquareRootAlg {
def next(a: Double)(x: Double): Double = (x + a/x)/2
def repeat[A](f: A=>A, a: A): Stream[A] = a #:: repeat(f, f(a))
#tailrec // Checks function is truly tail recursive.
def loopConditional[A](stop: (A, A) => Boolean)(s: => Stream[A] ): (A, Boolean) = {
val a = s.head
val t = s.tail
if(t.isEmpty) (a, false)
else if(stop(a, t.head)) (t.head, true)
else loopConditional(stop)(t)
}
}
Just return the booleans without modifying anything else:
object SquareRootAlg {
def next(a: Double)(x: Double): Double = (x + a/x)/2
def repeat[A](f: A => A, a: A): Stream[A] = a #:: repeat(f, f(a))
def loopConditionalInfo[A]
(stop: (A, A)=> Boolean)
(s: => Stream[A])
: (A, Boolean) = s match {
case a #:: t if t.isEmpty => (a, false)
case a #:: t =>
if (stop(a, t.head)) (t.head, true)
else loopConditionalInfo(stop)(t)
}
}
import SquareRootAlg._
val cond = (a: Double, b: Double) => (a-b).abs < 0.01
val alg = loopConditionalInfo(cond) _
val s = repeat(next(4.0), 4.0)
println(alg(s.take(3))) // = 2.05, "maxIters exceeded"
println(alg(s.take(5)))
prints
(2.05,false)
(2.0000000929222947,true)

How to do multiplication by using successor and predecessor in Scala

I am a beginner of programming language. Right now, I am working on a Scala project, which requires us to calculate the sum of the product and the exponentiation of two non-negative integers without using any Math functions and signs but only allows to use successor and predecessor. The functions do count for us. So I need to define addition in terms of those two integers, then define multiplication in terms of addition and exponents in terms of multiplication. So far, I've only come up with the solution for getting sum. Could you please help me to obtain the other parts? I think (if firstNum = 1, secondNum = 3) the product of these two can be obtained by using sum_1(sum_1(sum_1(a,0),a),a) But I really don't know how to write it in Scala code. Many thanks!
import io.StdIn._
val num = readLine("\n\nFor counting the sum, multiplication and exponentiation of two integers.\nPlease enter those two integers in (x,y) format: ")
val comma = num.indexOf(",")
val last = num.indexOf(")")
val firstNum = num.substring(1,comma).toInt
val secondNum = num.substring(comma+1,last).toInt
def sum_1(a:Int,b:Int): Int = {
def succ(a:Int): Int = a + 1
def pred(b:Int): Int = b - 1
if (b < 1) a
else {
succ(a)
pred(b)
sum_1(succ(a), pred(b))
}
}
//multiplication
//exponentation
println("1.The sum is " + sum_1(firstNum, secondNum) + ".")
println("2.The multiplication is .")
println("3.The exponentation is .")
You will have the following set of functions:
def succ(a:Int) = a+1
def pred(a:Int) = a-1
def sum(a:Int,b:Int):Int =
if(b<1) a
else sum(succ(a),pred(b))
def mul(a:Int,b:Int):Int =
if(b==0) 0
else if(b==1) a
else sum(mul(a,pred(b)),a)
def exp(a:Int, b:Int):Int =
if(b<1) 1
else mul(exp(a,pred(b)),a)
object mult extends App {
def succ(a: Int): Int = a + 1
def pred(b: Int): Int = b - 1
def sum(a: Int, b: Int): Int =
if (a == 0) b else sum(pred(a), succ(b))
def mult(a: Int, b: Int): Int =
if (a == 0) 0 else if (a == 1) b else sum(mult(pred(a), b), b)
def exp(a: Int, b: Int): Int =
if (b == 0) 1 else if (b == 1) a else mult(exp(a, pred(b)), a)
def printmult(a: Int, b: Int): Unit = println(s"$a * $b = ${mult(a, b)}")
(0 to 3).foreach { a => (0 to 3).foreach { b => printmult(a, b) } }
def printexp(a: Int, b: Int): Unit = println(s"$a ^ $b = ${exp(a, b)}")
(0 to 3).foreach { a => (0 to 3).foreach { b => printexp(a, b) } }
}

Simplest way to get the top n elements of a Scala Iterable

Is there a simple and efficient solution to determine the top n elements of a Scala Iterable? I mean something like
iter.toList.sortBy(_.myAttr).take(2)
but without having to sort all elements when only the top 2 are of interest. Ideally I'm looking for something like
iter.top(2, _.myAttr)
see also: Solution for the top element using an Ordering: In Scala, how to use Ordering[T] with List.min or List.max and keep code readable
Update:
Thank you all for your solutions. Finally, I took the original solution of user unknown and adopted it to use Iterable and the pimp-my-library pattern:
implicit def iterExt[A](iter: Iterable[A]) = new {
def top[B](n: Int, f: A => B)(implicit ord: Ordering[B]): List[A] = {
def updateSofar (sofar: List [A], el: A): List [A] = {
//println (el + " - " + sofar)
if (ord.compare(f(el), f(sofar.head)) > 0)
(el :: sofar.tail).sortBy (f)
else sofar
}
val (sofar, rest) = iter.splitAt(n)
(sofar.toList.sortBy (f) /: rest) (updateSofar (_, _)).reverse
}
}
case class A(s: String, i: Int)
val li = List (4, 3, 6, 7, 1, 2, 9, 5).map(i => A(i.toString(), i))
println(li.top(3, _.i))
My solution (bound to Int, but should be easily changed to Ordered (a few minutes please):
def top (n: Int, li: List [Int]) : List[Int] = {
def updateSofar (sofar: List [Int], el: Int) : List [Int] = {
// println (el + " - " + sofar)
if (el < sofar.head)
(el :: sofar.tail).sortWith (_ > _)
else sofar
}
/* better readable:
val sofar = li.take (n).sortWith (_ > _)
val rest = li.drop (n)
(sofar /: rest) (updateSofar (_, _)) */
(li.take (n). sortWith (_ > _) /: li.drop (n)) (updateSofar (_, _))
}
usage:
val li = List (4, 3, 6, 7, 1, 2, 9, 5)
top (2, li)
For above list, take the first 2 (4, 3) as starting TopTen (TopTwo).
Sort them, such that the first element is the bigger one (if any).
repeatedly iterate through the rest of the list (li.drop(n)), and compare the current element with the maximum of the list of minimums; replace, if neccessary, and resort again.
Improvements:
Throw away Int, and use ordered.
Throw away (_ > _) and use a user-Ordering to allow BottomTen. (Harder: pick the middle 10 :) )
Throw away List, and use Iterable instead
update (abstraction):
def extremeN [T](n: Int, li: List [T])
(comp1: ((T, T) => Boolean), comp2: ((T, T) => Boolean)):
List[T] = {
def updateSofar (sofar: List [T], el: T) : List [T] =
if (comp1 (el, sofar.head))
(el :: sofar.tail).sortWith (comp2 (_, _))
else sofar
(li.take (n) .sortWith (comp2 (_, _)) /: li.drop (n)) (updateSofar (_, _))
}
/* still bound to Int:
def top (n: Int, li: List [Int]) : List[Int] = {
extremeN (n, li) ((_ < _), (_ > _))
}
def bottom (n: Int, li: List [Int]) : List[Int] = {
extremeN (n, li) ((_ > _), (_ < _))
}
*/
def top [T] (n: Int, li: List [T])
(implicit ord: Ordering[T]): Iterable[T] = {
extremeN (n, li) (ord.lt (_, _), ord.gt (_, _))
}
def bottom [T] (n: Int, li: List [T])
(implicit ord: Ordering[T]): Iterable[T] = {
extremeN (n, li) (ord.gt (_, _), ord.lt (_, _))
}
top (3, li)
bottom (3, li)
val sl = List ("Haus", "Garten", "Boot", "Sumpf", "X", "y", "xkcd", "x11")
bottom (2, sl)
To replace List with Iterable seems to be a bit harder.
As Daniel C. Sobral pointed out in the comments, a high n in topN can lead to much sorting work, so that it could be useful, to do a manual insertion sort instead of repeatedly sorting the whole list of top-n elements:
def extremeN [T](n: Int, li: List [T])
(comp1: ((T, T) => Boolean), comp2: ((T, T) => Boolean)):
List[T] = {
def sortedIns (el: T, list: List[T]): List[T] =
if (list.isEmpty) List (el) else
if (comp2 (el, list.head)) el :: list else
list.head :: sortedIns (el, list.tail)
def updateSofar (sofar: List [T], el: T) : List [T] =
if (comp1 (el, sofar.head))
sortedIns (el, sofar.tail)
else sofar
(li.take (n) .sortWith (comp2 (_, _)) /: li.drop (n)) (updateSofar (_, _))
}
top/bottom method and usage as above. For small groups of top/bottom Elements, the sorting is rarely called, a few times in the beginning, and then less and less often over time. For example, 70 times with top (10) of 10 000, and 90 times with top (10) of 100 000.
Here's another solution that is simple and has pretty good performance.
def pickTopN[T](k: Int, iterable: Iterable[T])(implicit ord: Ordering[T]): Seq[T] = {
val q = collection.mutable.PriorityQueue[T](iterable.toSeq:_*)
val end = Math.min(k, q.size)
(1 to end).map(_ => q.dequeue())
}
The Big O is O(n + k log n), where k <= n. So the performance is linear for small k and at worst n log n.
The solution can also be optimized to be O(k) for memory but O(n log k) for performance. The idea is to use a MinHeap to track only the top k items at all times. Here's the solution.
def pickTopN[A, B](n: Int, iterable: Iterable[A], f: A => B)(implicit ord: Ordering[B]): Seq[A] = {
val seq = iterable.toSeq
val q = collection.mutable.PriorityQueue[A](seq.take(n):_*)(ord.on(f).reverse) // initialize with first n
// invariant: keep the top k scanned so far
seq.drop(n).foreach(v => {
q += v
q.dequeue()
})
q.dequeueAll.reverse
}
Yet another version:
val big = (1 to 100000)
def maxes[A](n:Int)(l:Traversable[A])(implicit o:Ordering[A]) =
l.foldLeft(collection.immutable.SortedSet.empty[A]) { (xs,y) =>
if (xs.size < n) xs + y
else {
import o._
val first = xs.firstKey
if (first < y) xs - first + y
else xs
}
}
println(maxes(4)(big))
println(maxes(2)(List("a","ab","c","z")))
Using the Set force the list to have unique values:
def maxes2[A](n:Int)(l:Traversable[A])(implicit o:Ordering[A]) =
l.foldLeft(List.empty[A]) { (xs,y) =>
import o._
if (xs.size < n) (y::xs).sort(lt _)
else {
val first = xs.head
if (first < y) (y::(xs - first)).sort(lt _)
else xs
}
}
You don't need to sort the entire collection in order to determine the top N elements. However, I don't believe that this functionality is supplied by the raw library, so you would have to roll you own, possibly using the pimp-my-library pattern.
For example, you can get the nth element of a collection as follows:
class Pimp[A, Repr <% TraversableLike[A, Repr]](self : Repr) {
def nth(n : Int)(implicit ord : Ordering[A]) : A = {
val trav : TraversableLike[A, Repr] = self
var ltp : List[A] = Nil
var etp : List[A] = Nil
var mtp : List[A] = Nil
trav.headOption match {
case None => error("Cannot get " + n + " element of empty collection")
case Some(piv) =>
trav.foreach { a =>
val cf = ord.compare(piv, a)
if (cf == 0) etp ::= a
else if (cf > 0) ltp ::= a
else mtp ::= a
}
if (n < ltp.length)
new Pimp[A, List[A]](ltp.reverse).nth(n)(ord)
else if (n < (ltp.length + etp.length))
piv
else
new Pimp[A, List[A]](mtp.reverse).nth(n - ltp.length - etp.length)(ord)
}
}
}
(This is not very functional; sorry)
It's then trivial to get the top n elements:
def topN(n : Int)(implicit ord : Ordering[A], bf : CanBuildFrom[Repr, A, Repr]) ={
val b = bf()
val elem = new Pimp[A, Repr](self).nth(n)(ord)
import util.control.Breaks._
breakable {
var soFar = 0
self.foreach { tt =>
if (ord.compare(tt, elem) < 0) {
b += tt
soFar += 1
}
}
assert (soFar <= n)
if (soFar < n) {
self.foreach { tt =>
if (ord.compare(tt, elem) == 0) {
b += tt
soFar += 1
}
if (soFar == n) break
}
}
}
b.result()
}
Unfortunately I'm having trouble getting this pimp to be discovered via this implicit:
implicit def t2n[A, Repr <% TraversableLike[A, Repr]](t : Repr) : Pimp[A, Repr]
= new Pimp[A, Repr](t)
I get this:
scala> List(4, 3, 6, 7, 1, 2, 8, 5).topN(4)
<console>:9: error: could not find implicit value for evidence parameter of type (List[Int]) => scala.collection.TraversableLike[A,List[Int]]
List(4, 3, 6, 7, 1, 2, 8, 5).topN(4)
^
However, the code actually works OK:
scala> new Pimp(List(4, 3, 6, 7, 1, 2, 8, 5)).topN(4)
res3: List[Int] = List(3, 1, 2, 4)
And
scala> new Pimp("ioanusdhpisjdmpsdsvfgewqw").topN(6)
res2: java.lang.String = adddfe
If the goal is to not sort the whole list then you could do something like this (of course it could be optimized a tad so that we don't change the list when the number clearly shouldn't be there):
List(1,6,3,7,3,2).foldLeft(List[Int]()){(l, n) => (n :: l).sorted.take(2)}
I implemented such an ranking algorithm recently in the Rank class of Apache Jackrabbit (in Java though). See the take method for the gist of it. The basic idea is to quicksort but terminate prematurely as soon as the top n elements have been found.
Here is asymptotically O(n) solution.
def top[T](data: List[T], n: Int)(implicit ord: Ordering[T]): List[T] = {
require( n < data.size)
def partition_inner(shuffledData: List[T], pivot: T): List[T] =
shuffledData.partition( e => ord.compare(e, pivot) > 0 ) match {
case (left, right) if left.size == n => left
case (left, x :: rest) if left.size < n =>
partition_inner(util.Random.shuffle(data), x)
case (left # y :: rest, right) if left.size > n =>
partition_inner(util.Random.shuffle(data), y)
}
val shuffled = util.Random.shuffle(data)
partition_inner(shuffled, shuffled.head)
}
scala> top(List.range(1,10000000), 5)
Due to recursion, this solution will take longer than some non-linear solutions above and can cause java.lang.OutOfMemoryError: GC overhead limit exceeded.
But slightly more readable IMHO and functional style. Just for job interview ;).
What is more important, that this solution can be easily parallelized.
def top[T](data: List[T], n: Int)(implicit ord: Ordering[T]): List[T] = {
require( n < data.size)
#tailrec
def partition_inner(shuffledData: List[T], pivot: T): List[T] =
shuffledData.par.partition( e => ord.compare(e, pivot) > 0 ) match {
case (left, right) if left.size == n => left.toList
case (left, right) if left.size < n =>
partition_inner(util.Random.shuffle(data), right.head)
case (left, right) if left.size > n =>
partition_inner(util.Random.shuffle(data), left.head)
}
val shuffled = util.Random.shuffle(data)
partition_inner(shuffled, shuffled.head)
}
For small values of n and large lists, getting the top n elements can be implemented by picking out the max element n times:
def top[T](n:Int, iter:Iterable[T])(implicit ord: Ordering[T]): Iterable[T] = {
def partitionMax(acc: Iterable[T], it: Iterable[T]): Iterable[T] = {
val max = it.max(ord)
val (nextElems, rest) = it.partition(ord.gteq(_, max))
val maxElems = acc ++ nextElems
if (maxElems.size >= n || rest.isEmpty) maxElems.take(n)
else partitionMax(maxElems, rest)
}
if (iter.isEmpty) iter.take(0)
else partitionMax(iter.take(0), iter)
}
This does not sort the entire list and takes an Ordering. I believe every method I call in partitionMax is O(list size) and I only expect to call it n times at most, so the overall efficiency for small n will be proportional to the size of the iterator.
scala> top(5, List.range(1,1000000))
res13: Iterable[Int] = List(999999, 999998, 999997, 999996, 999995)
scala> top(5, List.range(1,1000000))(Ordering[Int].on(- _))
res14: Iterable[Int] = List(1, 2, 3, 4, 5)
You could also add a branch for when n gets close to size of the iterable, and switch to iter.toList.sortBy(_.myAttr).take(n).
It does not return the type of collection provided, but you can look at How do I apply the enrich-my-library pattern to Scala collections? if this is a requirement.
An optimised solution using PriorityQueue with Time Complexity of O(nlogk). In the approach given in the update, you are sorting the sofar list every time which is not needed and below it is optimised by using PriorityQueue.
import scala.language.implicitConversions
import scala.language.reflectiveCalls
import collection.mutable.PriorityQueue
implicit def iterExt[A](iter: Iterable[A]) = new {
def top[B](n: Int, f: A => B)(implicit ord: Ordering[B]) : List[A] = {
def updateSofar (sofar: PriorityQueue[A], el: A): PriorityQueue[A] = {
if (ord.compare(f(el), f(sofar.head)) < 0){
sofar.dequeue
sofar.enqueue(el)
}
sofar
}
val (sofar, rest) = iter.splitAt(n)
(PriorityQueue(sofar.toSeq:_*)( Ordering.by( (x :A) => f(x) ) ) /: rest) (updateSofar (_, _)).dequeueAll.toList.reverse
}
}
case class A(s: String, i: Int)
val li = List (4, 3, 6, 7, 1, 2, 9, 5).map(i => A(i.toString(), i))
println(li.top(3, -_.i))

Why is my algorithm for Project Euler Problem 12 so slow?

I have created solution for PE P12 in Scala but is very very slow. Can somebody can tell me why? How to optimize this? calculateDevisors() - naive approach and calculateNumberOfDivisors() - divisor function has the same speed :/
import annotation.tailrec
def isPrime(number: Int): Boolean = {
if (number < 2 || (number != 2 && number % 2 == 0) || (number != 3 && number % 3 == 0))
false
else {
val sqrtOfNumber = math.sqrt(number) toInt
#tailrec def isPrimeInternal(divisor: Int, increment: Int): Boolean = {
if (divisor > sqrtOfNumber)
true
else if (number % divisor == 0)
false
else
isPrimeInternal(divisor + increment, 6 - increment)
}
isPrimeInternal(5, 2)
}
}
def generatePrimeNumbers(count: Int): List[Int] = {
#tailrec def generatePrimeNumbersInternal(number: Int = 3, index: Int = 0,
primeNumbers: List[Int] = List(2)): List[Int] = {
if (index == count)
primeNumbers
else if (isPrime(number))
generatePrimeNumbersInternal(number + 2, index + 1, primeNumbers :+ number)
else
generatePrimeNumbersInternal(number + 2, index, primeNumbers)
}
generatePrimeNumbersInternal();
}
val primes = Stream.cons(2, Stream.from(3, 2) filter {isPrime(_)})
def calculateDivisors(number: Int) = {
for {
divisor <- 1 to number
if (number % divisor == 0)
} yield divisor
}
#inline def decomposeToPrimeNumbers(number: Int) = {
val sqrtOfNumber = math.sqrt(number).toInt
#tailrec def decomposeToPrimeNumbersInternal(number: Int, primeNumberIndex: Int = 0,
factors: List[Int] = List.empty[Int]): List[Int] = {
val primeNumber = primes(primeNumberIndex)
if (primeNumberIndex > sqrtOfNumber)
factors
else if (number % primeNumber == 0)
decomposeToPrimeNumbersInternal(number / primeNumber, primeNumberIndex, factors :+ primeNumber)
else
decomposeToPrimeNumbersInternal(number, primeNumberIndex + 1, factors)
}
decomposeToPrimeNumbersInternal(number) groupBy {n => n} map {case (n: Int, l: List[Int]) => (n, l size)}
}
#inline def calculateNumberOfDivisors(number: Int) = {
decomposeToPrimeNumbers(number) map {case (primeNumber, exponent) => exponent + 1} product
}
#tailrec def calculate(number: Int = 12300): Int = {
val triangleNumber = ((number * number) + number) / 2
val startTime = System.currentTimeMillis()
val numberOfDivisors = calculateNumberOfDivisors(triangleNumber)
val elapsedTime = System.currentTimeMillis() - startTime
printf("%d: V: %d D: %d T: %dms\n", number, triangleNumber, numberOfDivisors, elapsedTime)
if (numberOfDivisors > 500)
triangleNumber
else
calculate(number + 1)
}
println(calculate())
You could first check what is slow. Your prime calculation, for instance, is very, very slow. For each number n, you try to divide n by each each number from 5 to sqrt(n), skipping multiples of 2 and 3. Not only you do not skip numbers you already know are not primes, but even if you fix this, the complexity of this algorithm is much worse than the traditional Sieve of Eratosthenes. See one Scala implementation for the Sieve here.
That is not to say that the rest of your code isn't suboptimal as well, but I'll leave that for others.
EDIT
Indeed, indexed access to Stream is terrible. Here's a rewrite that works with Stream, instead of converting everything to Array. Also, note the remark before the first if for a possible bug in your code.
#tailrec def decomposeToPrimeNumbersInternal(number: Int, primes: Stream[Int],
factors: List[Int] = List.empty[Int]): List[Int] = {
val primeNumber = primes.head
// Comparing primeNumberIndex with sqrtOfNumber didn't make any sense
if (primeNumber > sqrtOfNumber)
factors
else if (number % primeNumber == 0)
decomposeToPrimeNumbersInternal(number / primeNumber, primes, factors :+ primeNumber)
else
decomposeToPrimeNumbersInternal(number, primes.tail, factors)
}
Slow compared to....? How do you know it's an issue with Scala, and not with your algorithm?
An admittedly quick read of the code suggests you might be recalculating primes and other values over and over. isPrimeInternal jumps out as a possible case where this might be a problem.
Your code is not compilable, some parts are missing, so I'm guessing here. Some thing that frequently hurts performance is boxing/unboxing taking place in collections. Another thing that I noted is that you cunstruct your primes as a Stream - which is a good thing - but don't take advantage of this in your isPrime function, which uses a primitive 2,3-wheel (1 and 5 mod 6) instead. I might be wrong, but try to replace it by
def isPrime(number: Int): Boolean = {
val sq = math.sqrt(number + 0.5).toInt
! primes.takeWhile(_ <= sq).exists(p => number % p == 0)
}
My scala algorithm that calculates divisors of a given number. It worked fine in the solution of
Project Euler Problem 12.
def countDivisors(numberToFindDivisor: BigInt): Int = {
def countWithAcc(numberToFindDivisor: BigInt, currentCandidate: Int, currentCountOfDivisors: Int,limit: BigInt): Int = {
if (currentCandidate >= limit) currentCountOfDivisors
else {
if (numberToFindDivisor % currentCandidate == 0)
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors + 2, numberToFindDivisor / currentCandidate)
else
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors, limit)
}
}
countWithAcc(numberToFindDivisor, 1, 0, numberToFindDivisor + 1)
}
calculateDivisors can be greatly improved by only checking for divisors up to the square root of the number. Each time you find a divisor below the sqrt, you also find one above.
def calculateDivisors(n: Int) = {
var res = 1
val intSqrt = Math.sqrt(n).toInt
for (i <- 2 until intSqrt) {
if (n % i == 0) {
res += 2
}
}
if (n == intSqrt * intSqrt) {
res += 1
}
res
}

Resources