I am writing a program to count only the partitions of a number with distinct parts. I am using a bottom-up approach to dynamic programming to generate partition lists from previously obtained partition lists. I think my program runs correctly, as I have tested for some inputs and verified from OEIS. But it's extremely slow for n>15
. I think my algorithm has a complexity currently north of O(n^3)
, but I couldn't think of a better way to do it. Can anyone help with making it faster?
# Logic - The partition of a number 'n', will be 1 + the partition of 'n-1', 2 + the partition of 'n-2', and so on. # So we start from 1, and build partition lists based on previously gotten partitions # The loop doesn't have to go all the way till 1. # For example, for 6, we only have to go till 'n-3' and stop, because after that, we only get duplicate lists. This is not to say that we don't get # duplicates before, but after 'n-3', we get JUST duplicates # So we only have to go till n-x >= x from collections import Counter compare = lambda x, y: Counter(x) == Counter(y) def make_partitions(n): # Bottom up approach starts at 1 and goes till n, building on partitions already obtained # partitions dictionary contains list of lists of partitions # Ex - 1: [[1]] # Ex - 2: [[2], [1,1]] # Ex - 3: [[3], [2,1], [1,1,1]] partitions = {} start = 1 while start <= n: partitions[start] = [] # Appending the number itself as a partition of length 1 partitions[start].append([start]) # prev stores the number currently being used to build the partition list prev = start - 1 # pp stores all partition lists obtained so far for the current number pp = [] while prev >= start-prev: # curr_partitions stores the partition lists that make up the desired number, FROM the current number # Ex - for desired number 6, in first loop, it stores those lists which make up 6 from those of 5; in the second loop, from those of 4 and so on curr_partitions = [] prev_partitions = partitions[prev] for p in prev_partitions: q = list(p) q.append(start-prev) # self-explanatory. compare function is used to see if the list already exists does_exist_already = False for ppp in pp: if compare(q, ppp): does_exist_already = True if not does_exist_already: curr_partitions.append(q) # We have got the entire list of partitions that make up the desired number FROM the current number, so we add to the dictionary partitions[start].extend(curr_partitions) prev -= 1 pp.extend(curr_partitions) start += 1 return partitions def answer(n): partitions = make_partitions(n) req_partition_list = partitions[n] final_partition_list = [] count = 0 # This for loop is to weed out lists which contain duplicate values for p in req_partition_list: c = Counter(p) if all(v==1 for v in c.values()): final_partition_list.append(p) count += 1 return count if __name__ == '__main__': n = int(raw_input()) print answer(n)