The integers can be partitioned by representing a number as the sum of positive integers (ignoring order). The task is to compute all possible integer partitions of a given number n.
One important observation is that permutations of the summands in a partition do not create new, distinct partitions. For example, both 2+3 and 3+2 represent the same partition of 5. Therefore, to find all unique partitions of an integer N, we need to ensure our algorithm respects this rule.
While generating functions can mathematically solve this problem (refer to Wikipedia’s entry for details), implementing such functions algorithmically is complex. A simpler approach is to use a recursive divide-and-conquer method. This means breaking the problem into smaller subproblems recursively to find all possible partitions.
Input 1:sum - The target sum we are trying to partition.
Input 2:largestNumber - The largest number allowed in a partition for the current recursive call.
Using these variables, the function dynamically adjusts the range of integers it can use to form partitions, ensuring uniqueness by keeping the numbers in progressively non-increasing order.
classSolution {
public:int helper(int sum, int largestNumber) {
// Base Cases
if (sum ==0)
return1; // Found one valid partition
if (sum <0|| largestNumber ==0)
return0; // No valid partition
// Divide-and-Conquer: Include the largestNumber OR exclude it
int includeLargest = helper(sum - largestNumber, largestNumber);
int excludeLargest = helper(sum, largestNumber -1);
return includeLargest + excludeLargest; // Combine both groups
}
intintegerPartition(int n) {
return helper(n, n); // Call the helper function
}
};
// Example usage
intmain() {
Solution solution;
int n =5;
cout <<"Number of partitions for "<< n <<": "<< solution.integerPartition(n) << endl;
return0;
}
classSolution:
defintegerPartition(self, n):
# Recursive function to compute partitionsdefhelper(sum, largestNumber):
# Base Casesif sum ==0:
return1# Found one valid partitionif sum <0or largestNumber ==0:
return0# No valid partition# Divide-and-Conquer: Include the largestNumber OR exclude it includeLargest = helper(sum - largestNumber, largestNumber)
excludeLargest = helper(sum, largestNumber -1)
return includeLargest + excludeLargest # Combine both groups# Call the helper functionreturn helper(n, n)
# Example usage:solution = Solution()
n =5print(f"Number of partitions for {n}: {solution.integerPartition(n)}")
⏰ Time complexity: O(2^n) for given n. The naive approach splits the problem into two recursive branches at each step (largestNumber being included or excluded), resulting in exponential growth.
🧺 Space complexity: O(n). Each recursive call consumes stack space. In the worst case (with no base cases reached), the recursion depth equals n.
Method 2 - Using Top-down DP approach using memoization#
Memoization optimises the naive recursive solution by storing previously computed results in a hash map or table. This avoids redundant recursive calls and significantly reduces the time complexity.
Here is the approach:
Memoization Table:
Use a 2D data structure (memo[sum][largestNumber]) to store results:
sum is the remaining sum we want to partition.
largestNumber is the largest number currently allowed in the partition.
Recursive Computation:
For each (sum, largestNumber), compute the partitions by:
Including the current largest number.
Excluding the current largest number.
Base Cases:
Same as the naive recursive solution:
If sum == 0, a valid partition is found → Return 1.
If sum < 0 or largestNumber == 0, no valid partition → Return 0.
classSolution {
private: unordered_map<string, int> memo; // Memoization dictionary
// Private helper function with memoization
inthelper(int sum, int largestNumber) {
// Create memoization key
string key = to_string(sum) +","+ to_string(largestNumber);
if (memo.find(key) != memo.end())
return memo[key];
// Base cases
if (sum ==0)
return1; // Found a valid partition
if (sum <0|| largestNumber ==0)
return0; // No valid partition
// Recursive computation with memoization
int includeLargest = helper(sum - largestNumber, largestNumber);
int excludeLargest = helper(sum, largestNumber -1);
memo[key] = includeLargest + excludeLargest;
return memo[key];
}
public:// Public method to start partitioning
int integerPartition(int n) {
returnhelper(n, n); // Start recursion with the full range
}
};
// Example usage
intmain() {
Solution solution;
int n =5;
cout <<"Number of partitions for "<< n <<": "<< solution.integerPartition(n) << endl;
return0;
}
classSolution:
defintegerPartition(self, n):
# Initialize memo dictionary memo = {}
# Private helper function with memoizationdefhelper(sum, largestNumber):
# Use memoization key for current subproblem key = (sum, largestNumber)
if key in memo:
return memo[key]
# Base casesif sum ==0:
return1# Found a valid partitionif sum <0or largestNumber ==0:
return0# No valid partition# Recursive computation with memoization includeLargest = helper(sum - largestNumber, largestNumber)
excludeLargest = helper(sum, largestNumber -1)
memo[key] = includeLargest + excludeLargest
return memo[key]
return helper(n, n)
# Example usagesolution = Solution()
n =5print(f"Number of partitions for {n}: {solution.integerPartition(n)}")
classSolution {
public:int integerPartition(int sum, int largestNumber) {
// Initialize DP table
vector<vector<int>> dp(sum +1, vector<int>(largestNumber +1, 0));
// Base cases
for (int j =0; j <= largestNumber; j++) {
dp[0][j] =1; // There is one way to partition sum=0
}
for (int i =1; i <= sum; i++) {
for (int j =1; j <= largestNumber; j++) {
dp[i][j] = dp[i][j -1]; // Exclude the current largest number
if (i - j >=0) {
dp[i][j] += dp[i - j][j]; // Include the current largest number
}
}
}
return dp[sum][largestNumber];
}
};
// Example usage
intmain() {
Solution solution;
int sum =100;
int largestNumber =99;
cout <<"Number of partitions for sum="<< sum <<" with largestNumber="<< largestNumber <<": "<< solution.integerPartition(sum, largestNumber) << endl;
return0;
}
publicclassSolution {
publicintintegerPartition(int sum, int largestNumber) {
// Initialize DP tableint[][] dp =newint[sum + 1][largestNumber + 1];
// Base casesfor (int j = 0; j <= largestNumber; j++) {
dp[0][j]= 1; // There is one way to partition sum=0 }
for (int i = 1; i <= sum; i++) {
for (int j = 1; j <= largestNumber; j++) {
dp[i][j]= dp[i][j - 1]; // Exclude the current largest numberif (i - j >= 0) {
dp[i][j]+= dp[i - j][j]; // Include the current largest number }
}
}
return dp[sum][largestNumber];
}
// Example usagepublicstaticvoidmain(String[] args) {
Solution solution =new Solution();
int sum = 100;
int largestNumber = 99;
System.out.println(
"Number of partitions for sum="+ sum +" with largestNumber="+ largestNumber +": "+ solution.integerPartition(sum, largestNumber));
}
}
classSolution:
defintegerPartition(self, sum, largestNumber):
# Initialize DP table dp = [[0for _ in range(largestNumber +1)] for _ in range(sum +1)]
# Base casesfor i in range(largestNumber +1):
dp[0][i] =1# There is one way to partition sum=0for i in range(1, sum +1):
for j in range(1, largestNumber +1):
dp[i][j] = dp[i][j -1] # Exclude the current largest numberif i - j >=0:
dp[i][j] += dp[i - j][j] # Include the current largest numberreturn dp[sum][largestNumber]
# Example usagesolution = Solution()
sum =100largestNumber =99print(f"Number of partitions for sum={sum} with largestNumber={largestNumber}: {solution.integerPartition(sum, largestNumber)}")