|
3 | 3 |
|
4 | 4 | import numbers |
5 | 5 | import collections |
6 | | - |
| 6 | +import itertools |
7 | 7 |
|
8 | 8 | infinity = float('inf') |
9 | 9 |
|
@@ -60,29 +60,60 @@ def abs_stirling(n, k): |
60 | 60 |
|
61 | 61 | _shitfuck_cache = {} |
62 | 62 |
|
63 | | -def get_sub_counters(counter): |
| 63 | +def get_sub_counters_counter(counter): |
| 64 | + from python_toolbox import nifty_collections |
64 | 65 | assert isinstance(counter, nifty_collections.FrozenCounter) |
65 | | - return { |
| 66 | + return nifty_collections.FrozenCounter({ |
66 | 67 | nifty_collections.FrozenCounter( |
67 | | - {key: (value - (key == key_to_reduce)) for key, value in self.items()} |
68 | | - ) for key_to_reduce in self |
69 | | - } |
| 68 | + {key: (value - (key == key_to_reduce)) for |
| 69 | + key, value in counter.items()} |
| 70 | + ): value_of_key_to_reduce |
| 71 | + for key_to_reduce, value_of_key_to_reduce in counter |
| 72 | + }) |
70 | 73 |
|
71 | 74 |
|
72 | 75 | def shitfuck(k, recurrence_counter): |
73 | 76 | from python_toolbox import nifty_collections |
74 | | - assert isinstance(recurrence_counter, nifty_collections.FrozenCounter) |
| 77 | + from python_toolbox import cute_iter_tools |
| 78 | + if not isinstance(recurrence_counter, nifty_collections.FrozenCounter): |
| 79 | + recurrence_counter = \ |
| 80 | + nifty_collections.FrozenCounter(recurrence_counter) |
| 81 | + if k == 1: |
| 82 | + assert recurrence_counter # Works because `FrozenCounter` implements |
| 83 | + # `__bool__`. |
| 84 | + return 1 |
| 85 | + try: |
| 86 | + return _shitfuck_cache[(k, recurrence_counter)] |
| 87 | + except KeyError: |
| 88 | + pass |
| 89 | + |
75 | 90 | levels = [] |
76 | 91 | current_reccurence_counters = {recurrence_counter} |
77 | | - while len(levels) < k and any(((k - len(levels) + 1), recurrence_counter) |
78 | | - not in _shitfuck_cache for x in levels[-1]): |
79 | | - new_level = {} |
80 | | - for recurrence_counter_ in current_reccurence_counters: |
81 | | - new_level[recurrence_counter_] = |
82 | | - for smaller_recurrence_counter in recurrence_counter_.counters_with_one_removed: |
83 | | - new_level[(k - len(levels), smaller_recurrence_counter)] += factor |
84 | | - levels.append(new_level) |
| 92 | + while len(levels) < k and current_reccurence_counters: |
| 93 | + levels.append( |
| 94 | + {recurrence_counter_: get_sub_counters_counter(recurrence_counter_) |
| 95 | + for recurrence_counter_ in current_reccurence_counters |
| 96 | + if recurrence_counter_ not in _shitfuck_cache |
| 97 | + }) |
| 98 | + current_reccurence_counters = set(itertools.chain(levels[-1].values())) |
| 99 | + |
85 | 100 | # The last level is calculated. Time to make our way up. |
86 | | - if len(levels) == k: |
| 101 | + for k_, level in enumerate(reversed(levels), (k - len(levels) + 1)): |
| 102 | + if k_ == 1: |
| 103 | + continue |
| 104 | + elif k_ == 2: |
| 105 | + for recurrence_counter_, sub_counters_counter in \ |
| 106 | + levels.pop().items(): |
| 107 | + _shitfuck_cache[(k_, recurrence_counter_)] = \ |
| 108 | + sub_counters_counter.n_elements |
| 109 | + else: |
| 110 | + for recurrence_counter_, sub_counters_counter in level.items(): |
| 111 | + _shitfuck_cache[(k_, recurrence_counter_)] = sum( |
| 112 | + (_shitfuck_cache[(k_ - 1, sub_counter)] * factor for |
| 113 | + sub_counter, factor in sub_counters_counter) |
| 114 | + ) |
| 115 | + |
| 116 | + return _shitfuck_cache[(k, recurrence_counter)] |
87 | 117 |
|
| 118 | + |
88 | 119 |
|
0 commit comments