Reputation: 6394
I am trying to count in how many ways I can partition an integer. So far I came up with the following function:
def partition(num: Int): Int = {
if(num == 1) return 1
if(num <= 0) return 0
return partition(num-1) + partition(num-(num-1))
}
partition(6) //6 instead of 7
E.g.: 5 -> 4 + 1, 3 + 2, 2 + 2 + 1, 2 + 1 + 1 + 1, 1 + 1 + 1 + 1 + 1
It returns 1 if num
is 1 because I think partition(1)
is the end.
Maybe you can spot the logic error in it?
Upvotes: 1
Views: 2049
Reputation: 2248
def partition(n: Int): Int = {
def partDecr(n: Int, decr: Int): Int = {
if (n == 0) 1
else if (n < 0 || decr == 0) 0
else partDecr(n - decr, decr) + partDecr(n, decr - 1)
}
partDecr(n, n)
}
Note this is not tail recursive and can use quite some stack space, but it is so slow it doesn't matter much.
Upvotes: 2
Reputation: 49
I think this works:
def partition(n: Int): Int = {
def inner(n: Int, max: Int): Int =
if (n == 0) 1
else if (n < 0) 0
else ((1 to max) map (x => inner(n - x, x))).sum
if (n == 0) 0
else inner(n, n-1)
}
An integer n
can be partitioned into (n-1) + (any way of partitioning 1)
, (n-2) + (any way of partitioning 2)
, ..., 1 + (any way of partitioning (n-1))
. However, naïvely computing partition(m)
for m = 1 to n-1
and summing the results will count some methods of partitioning n
twice, e.g. 1 + (n-1)
and (n-1) + 1
.
We can solve this problem by viewing a partition as a sequence of positive integers i{j} < n
that sum to n
, and only considering sequences that are ordered. The inner
method has a max
parameter that ensures it will only consider sequences with i{j} >= i{j+1}
. So it will consider e.g. 2 + 1
, but not 1 + 2
.
n == 0
is an annoying edge case in the above code because you effectively don't want to count the empty sequence.
Upvotes: 4
Reputation: 41646
I think it requires another argument (the integers that can make up the partition, call it madeOf
). That way you can divide the problem in 2 strictly smaller subsets. The count of partition(num, madeOf=(n, n-1, ..., 1))
is the sum of
partition(num, madeOf=(n-1, ..., 1))
(all the partitions that don't contain n)partition(num-n, madeOf(n, n-1, ..., 1))
(all the partitions where there is at least one n)You can then make it more optimal, since madeOf
can be constructed from only one int:
def part(num: Int, madeOf: Int): Int =
if (num == 0) 1 // we found a partition!
else if (num < 0) 0 // no partition...
else if (madeOf == 0) 0 // no more ways to make partition
else part(num, madeOf - 1) + // all the partitions that don't contain n
part(num - madeOf, madeOf) // all the partition that contain n
part(5, 4) // 6
Upvotes: 1
Reputation: 2438
You can use the following inner function - you only need to create a list with numbers < the number you want to partition. For example you want to partition 4, then intNumber should be (1,2,3,4)
def partition(number: Int, intNumbers: List[Int]): Int = {
if (number <= 0 || intNumbers.isEmpty)
0
else if ((number-intNumbers.head)==0)
1+partition(number-intNumbers.head,intNumbers)+partition(number,intNumbers.tail)
else
partition(number-intNumbers.head,intNumbers)+partition(number,intNumbers.tail)
Upvotes: 0
Reputation: 28688
I think there is no "that simple" algorithm to compute what you want. The presented algorithm in the OP won't work for a couple of reasons (which I won't discuss).
But I direct you to the Wikipedia entry on 'Partition', which also includes a recursive formula.
Notice, that this exact formula is both computationally more complex and also more complicated than the presented algorithm in the OP.
Upvotes: 1