from functools import lru_cache
@lru_cache(None)
def countNK(n,k,t=None):
t = n if t is None else t # track target partial sum
if k == 0: return int(t==0) # empty set can sum to zero
if t < 1 : return 0 # valid target only
if k > n : return 0 # not enough values
return countNK(n-1,k,t)+countNK(n-1,k-1,t-n) # combine counts
def Kelly(n, k):
if k == 0:
return 1 if n == 0 else 0
@cache
def c(n, k):
if n < k * (k+1) // 2:
return 0
if k == 1:
return 1
n -= k
return c(n, k) + c(n, k-1)
return c(n, k)
# Precompute the bounds for the "n < ..." base case
def Kelly2(n, k):
if k == 0:
return 1 if n == 0 else 0
min_n_for_k = list(accumulate(range(k+1)))
@cache
def c(n, k):
if n < min_n_for_k[k]:
return 0
if k == 1:
return 1
n -= k
return c(n, k) + c(n, k-1)
return c(n, k)
def Alain(n, k):
@lru_cache(None)
def countNK(n,k,t=None):
t = n if t is None else t # track target partial sum
if k == 0: return int(t==0) # empty set can sum to zero
if t < 1 : return 0 # valid target only
if k > n : return 0 # not enough values
return countNK(n-1,k,t)+countNK(n-1,k-1,t-n) # combine counts
return countNK(n, k)
def Dave_translated_by_Kelly(n, k):
def choose(n, k):
if k > n: return 0
result = 1
for d in range(1, k+1):
result *= n
result //= d
n -= 1
return result
def count_partitions_nozeroes(n, k, cache = {}):
if k==0 and n==0: return 1
if n <= 0 or k <= 0: return 0
# Check if the result is already memoized
if (n, k) in cache:
return cache[n, k]
# Calculate the result
result = count_partitions_nozeroes(n-1, k-1, cache) + count_partitions_nozeroes(n-k, k, cache)
# Memoize the result for future use
cache[n, k] = result
return result
def count_partitions_zeros(n,k):
return count_partitions_nozeroes(n+k, k)
def solve(n,k):
r = n - choose(k+1,2)
return count_partitions_zeros(r,k)
return solve(n, k)
big = False
funcs = Alain, Kelly, Kelly2, Dave_translated_by_Kelly
if big:
funcs = funcs[1:]
from functools import lru_cache, cache
from itertools import accumulate
from time import perf_counter as time
from statistics import mean, stdev
import sys
import gc
# Correctness
for n in range(51):
for k in range(51):
expect = funcs[0](n, k)
for f in funcs[1:]:
result = f(n, k)
assert result == expect
# Speed
sys.setrecursionlimit(20000)
times = {f: [] for f in funcs}
def stats(f):
ts = [t * 1e3 for t in sorted(times[f])[:5]]
return f'{mean(ts):5.1f} ± {stdev(ts):4.1f} ms '
for _ in range(25):
for f in funcs:
gc.collect()
t0 = time()
if big:
f(9000, 100)
else:
for k in range(101):
f(100, k)
times[f].append(time() - t0)
for f in sorted(funcs, key=stats):
print(stats(f), f.__name__)
我们可以用k个不同的正整数组成的最小数是choose(k+1,2)。 设r(n,k)= n - choose(k+1,2). 那么从k个不同的整数形成n的方法的计数等于将k个非负的不一定不同的整数求和以得到r(n,k)的方法的计数。我们的想法是从1,2,3,...,k开始,然后以非递减的方式将r(n,k)分配给这些起始整数。 例如,10、3: 所以我们把问题简化为计算k个非负整数求和得到r(n,k)的方法的数量。已回答here Ruby代码(包括util函数):
def choose(n, k)
return 0 if k > n
result = 1
1.upto(k) do |d|
result *= n
result /= d
n -= 1
end
return result
end
def count_partitions_nozeroes(n, k, cache = {})
return 1 if k==0 && n==0
return 0 if n <= 0 || k <= 0
# Check if the result is already memoized
if cache.key?([n, k])
return cache[[n, k]]
end
# Calculate the result
result = count_partitions_nozeroes(n-1, k-1, cache) + count_partitions_nozeroes(n-k, k, cache)
# Memoize the result for future use
cache[[n, k]] = result
return result
end
def count_partitions_zeros(n,k)
return count_partitions_nozeroes(n+k, k)
end
def solve(n,k)
r = n - choose(k+1,2)
return count_partitions_zeros(r,k)
end
4条答案
按热度按时间rkttyhzu1#
使用缓存的递归方法可以在合理的时间内产生结果:
n
值来瞄准目标...
输出:
n
值(例如500+),你需要增加递归限制或者将函数转换为迭代循环 *vdzxcuhz2#
基准测试n=100,所有k从0到100,
Kelly*
是我的解决方案:令c(n,k)是具有和n的组合的数量,其中k个不同的数为1或更大。
我们得到:
c(n, k) = c(n-k, k) + c(n-k, k-1)
你想和n与k个不同的数字1或更大。你要么用数字1,要么不用。
Dave的情况n=9000,k=100的更快的解决方案:
代码(Attempt This Online!):
cunj1qz13#
我们可以用k个不同的正整数组成的最小数是choose(k+1,2)。
设r(n,k)= n - choose(k+1,2).
那么从k个不同的整数形成n的方法的计数等于将k个非负的不一定不同的整数求和以得到r(n,k)的方法的计数。我们的想法是从1,2,3,...,k开始,然后以非递减的方式将r(n,k)分配给这些起始整数。
例如,10、3:
所以我们把问题简化为计算k个非负整数求和得到r(n,k)的方法的数量。已回答here
Ruby代码(包括util函数):
样品结果
这里有一个更简单的Ruby版本,它避免了递归(其他方法不变)。它给出了与上面相同的结果。下面显示了较大数字的一些结果。时间复杂度为O(n)。
样品结果
6qqygrtg4#
让我们引入一个函数:
f(n,k,s)
=从1到n
的k
的组合数,s
作为它们的总和。为了解决这个问题,我们需要计算
f(n,k,n)
。可以递归地计算该函数。所有组合可分为两组:有和没有最大值。这就是
f(n,k,s)=f(n-1,k-1,s-n)+f(n-1,k,s)
。在以下情况下,递归可能会停止:有
N^2*k
可能的参数组合,因此如果我们缓存已经计算的值,我们将在O(N^3)
内。