with the following code, I count the restricted integer partitions(each number can only occure once in each partition) with k numbers in each partition, each number is equal or greater than 1 and not greater than m. This code generate a lot of cache values so that it goes out memory quickly.
Example:
sum := 15, k := 4, m:= 10 expected result is 6
Has following restricted integer partitions:
1,2,3,9,1,2,4,8,1,2,5,7,1,3,4,7,1,3,5,7,2,3,4,6
public class Key{
private final int sum;
private final short k1;
private final short start;
private final short end;
public Key(int sum, short k1, short start, short end){
this.sum = sum;
this.k1 = k1;
this.start = start;
this.end = end;
}
// + hashcode and equals
}
public BigInteger calcRestrictedIntegerPartitions(int sum,short k,short m){
return calcRestrictedIntegerPartitionsHelper(sum,(short)0,k,(short)1,m,new HashMap<>());
}
private BigInteger calcRestrictedIntegerPartitionsHelper(int sum, short k1, short k, short start, short end, Map<Key,BigInteger> cache){
if(sum < 0){
return BigInteger.ZERO;
}
if(k1 == k){
if(sum ==0){
return BigInteger.ONE;
}
return BigInteger.ZERO;
}
if(end*(k-k1) < sum){
return BigInteger.ZERO;
}
final Key key = new Key(sum,(short)(k-k1),start,end);
BigInteger fetched = cache.get(key);
if(fetched == null){
BigInteger tmp = BigInteger.ZERO;
for(short i=start; i <= end;i++){
tmp = tmp.add(calcRestrictedIntegerPartitionsHelper(sum-i,(short)(k1+1),k,(short)(i+1),end,cache));
}
cache.put(key, tmp);
return tmp;
}
return fetched;
}
Is there formula to avoid/reduce caching? Or how Can I count restricted integer partions with k and m?
Your problem can be transposed, so you only need 3 keys in your cache and a lot less runtime to boot. Less distinct keys means better caching (A smarter person than me may still find a cheaper solution).
Let's view the partitions as sets. The elements of each set shall be ordered (ascending). You have already done this implicitly, when you stated the expected results for
sum := 15, k := 4, m:= 10as[1, 2, 3, 9]; [1, 2, 4, 8] ....The restrictions you defined for the partitions are:
kelements per setmas elementThe restriction of distinction is actually a bit bothersome, so we will lift it. For that, we need to transform the problem a bit. Because the elements of your set are ascending (and distinct), we know, that the minimum value of each element is an ascending sequence (if we ignore that the sum must be
sum), so the minia are:[1, 2, 3, ...]. Ifmwere for example less thank, then the number of possible partitions would always be zero. Likewise, if the sum of[1, 2, 3, ... k]is more thansum, then you also have zero results. We exclude these edge cases at the beginning, to make sure the transformation is legal.Let us look at a geometric representation of a 'legal partition' and how we want to transform it. We have
kcolumns,mrows andsumsquares are filled blue (either light or dark blue).The red and dark blue squares are irrelevant, as we already know, the dark blue squares must always be filled, and the red ones must always be empty. Therefore we can exclude them from our calculation and assume their respective states as we go along. The resulting box is represented on the right side. Every column was 'shifted down' by it's position, and the red and dark blue areas are cut off. We now have a smaller overall box and a column can now be empty (and we may have the same number of blue boxes among neighboring columns).
Algorithmically the transformation now works like this: For every element in a legal partition, we subtract it's position (starting at 1). So for
[1, 2, 4, 8]we get[0, 0, 1, 4]. Furthermore, we have to adapt our bounds (sumandm) accordingly:Now we have transposed our partitioning problem into another partitioning problem, one that does not have the restriction of distinct elements! Also, this partition can contain element
0, which our original could not. (We keep the internal ascending order).Now we need to refine the recursion a bit. If we know the elements are ascending, not necessariely distinct and always less-equal to
m_2, then we have bound the possible elements to a range. Example:Because we know that
n1andn2in the example are3or greater, when calling the recursion, we can also instead reduce them both by3and reducesum_2by2 * 3(one is the number of 'open' elements, one is the value of the last 'fixed' element). This way, what we pass in the recursion does not have an upper and a lower bound, but only an upper bound, which is what we had before (m).Because of this, we can toss 1 value of your cache key:
start. Instead we now only have 3:sum,mandk, when solving this reduced problem.The following implementation works to this effect:
Note: I used apache-common's
Triple-class as key here, to spare the implementation of an explicit key-class, but this is not an optimization in runtime, it just saves code.Edit: Beside a fix to a problem found by @MBo (thank you), I added a few shortcuts to reach the same result. The algorithm now performs even better, and the cache (reuse) rate is better. Maybe this will satisfy your requirements?
The optimizations explained (they are only applicable after the above mentioned transposition of the problem):
k > m, we can 'flip' the rectangle upright, and still get the same result for the number of legal partitions. This will map some 'lying' configurations into 'upright' configurations and reduce the overall amount of different keys.sum < kand/orsum < m, we can reducekand/ormto sum, and still get the same number of partitions. (this is the most impacting optimization, as it often skips multiple redundant interim steps and frequently reachesm = k = sum)