I need to get the distinct sums of naturals of a natural
What I came up with is:
type Sum = Stream[Int] //should maintain the invariant that
//* all Ints are positive
//* is non-strictly decreasing
//* is non-empty
def makesums(i: Int): Stream[Sum] = {
/* Schmear will try to "schmear" a sum by decreasing the first number by one,
and adding one again in the first position that won't violate the above invariants.
This is possible when the stream is not all 1's, and the above invariants are met for
the argument.
returns an Option[Sum], which is Some(schmeared) if the above is possible,
and None otherwise.
*/
def schmear(sum: Sum): Option[Sum] = sum match {
//if we can decrease the head and increase the next element while
//staying ordered, do that
case head #:: s #:: tail if (head - 1 >= s + 1) =>
Some((head - 1) #:: (s + 1) #:: tail)
//otherwise, if the head is only one larger than the second element, do the same,
//but smear the tail, restoring the invariant
case head #:: s #:: tail if head > s =>
schmear((s + 1) #:: tail).map(nt => (head - 1) #:: nt)
//otherwise, if there are at least two elements, just schmear the tail,
//and keep the orignal head
case head #:: s #:: tail =>
schmear(s #:: tail).map(nt => head #:: nt)
//otherwise, if the head is larger than 1, decrease by 1, and put a 1 at the end
case head #:: tail if (head > 1) =>
Some((head - 1) #:: tail #::: Stream(1))
//otherwise, it's not possible.
case _ => None
}
def rec(sum: Sum): Stream[Sum] = {
schmear(sum) match {
case Some(schmeared) => sum #:: rec(schmeared)
case None => Stream(sum)
}
}
//initiate the recursive algorithm with the sum of the identity
rec(Stream(i))
}
Using this:
println(makesums(5).map(_.toList).toList)
List(List(5), List(4, 1), List(3, 2), List(2, 2, 1), List(2, 1, 1, 1), List(1, 1, 1, 1, 1))
Going through the algorithm:
Stream(5)
takes case 4, yieldingStream(4, 1)
Stream(4, 1)
takes case 1, yieldingStream(3, 2)
Stream(3, 2)
takes case 2, starting with(3 - 1) = 2
, and recurses withStream(2 + 1)
for the tailStream(3)
takes case 4, yieldingStream(2, 1)
- yielding
Stream(2, 2, 1)
Stream(2, 2, 1)
takes case 3, starting with2
, and recurses withStream(2, 1)
Stream(2, 1)
takes case 2, starting with(2 - 1) = 1
, and recurses withStream(2)
for the tailStream(2)
takes case 4, yieldingStream(1, 1)
- yielding
Stream(1, 1, 1)
- yielding
Stream(2, 1, 1, 1)
Stream(2, 1, 1, 1)
takes case 2, taking1
for the head, and recursing withStream(2, 1, 1)
for the tail- keep taking case 2 until you heave
Stream(2)
Stream(2)
takes case 4, yieldingStream(1, 1)
- keep taking case 2 until you heave
- yielding
Stream(1, 1, 1, 1, 1)
but if feels really complicated. In particular, having 5 different cases for the schmearing, which seems way more complicated than describing what you do.
What can I do to simplify this?