可以使用两个跨度、四个跨度、八个跨度等(其中跨度是包括其端点的整数范围)来计算使每个可能的总数的方法的数量。将这些数字列在表格中,您可以向后工作以获取样本。例如,假设有 16 个跨度,每个跨度都包括数字 1 到 9。人们发现有 w = 202752772954792 种方法可以使t = 100
. 在 1 到 w 的范围内选择一个随机数 r。搜索(或查找)以找到一个数字 J,使得 J>j 的leftways(t-j)*rightways(j))
总和超过 r,而 J>j+1 的总和不超过,其中leftways(i)
是使用前 8 个跨度生成 i 的方法的数量,以及rightways(j)
是使用最后 8 个跨度生成 j 的方法数。使用 i = tj 与前 8 个跨度和 j 与最后 8 个等进行递归。只要只有一种方法可以得出所需的总数,就会出现基本情况。
下面的代码可以通过按宽度降序排序跨度(即首先列出最宽的跨度)并删除一些交换来修改以更有效地运行。请注意,如果 span 不是按宽度降序排列的,则结果向量的顺序将与 spans 不同。此外,可能有可能用二分搜索替换线性for j ...
搜索,findTarget
但我还没有弄清楚如何在不使用更多空间的情况下这样做。
通过使用对象来存储树结构,而不仅仅是元组,代码可以变得更清晰、更清晰。
使用的空间可能是O(s²·(lg m))
如果有 m 个跨度的最大值总计达到 s。所用时间用于O(s²·(lg m))
产品总和的初始制表,并且可能O(m+(lg m)·(s/m))
用于O(m+(lg m)·s)
每个样本。(我没有正确分析空间和时间要求。)在 2GHz 机器上,如果有 16 个相同的跨度 1...10,所示代码每秒产生大约 8000 个样本;如果有 32 个相同的跨度 1...3,则每秒大约 5000 个样本;如果有 32 个相同的跨度 1...30,则每秒大约 2000 个样本。代码后显示了各种目标总数和跨度集的一些示例输出。
from random import randrange
def randx(hi): # Return a random integer from 1 to hi
return 1+randrange(hi) if hi>0 else 0
# Compute and return c with each cell k set equal to
# sum of products a[k-j] * b[j], taken over all relevant j
def sumprods(lt, rt):
a, b = lt[0], rt[0]
(la,ma,aa), (lb,mb,bb) = a, b
if ma-la < mb-lb: # Swap so |A| >= |B|
la, ma, aa, lb, mb, bb = lb, mb, bb, la, ma, aa
lc, mc = la+lb, ma+mb
counts = [0]*(mc+1)
for k in range(lc,mc+1):
for j in range(max(lb,k-ma), min(mb,k-la)+1):
counts[k] += aa[k-j] * bb[j]
return (lc, mc, counts)
def maketree(v):
lv = len(v)
if lv<2:
return (v[0], None, None)
ltree = maketree(v[:lv/2])
rtree = maketree(v[lv/2:])
return (sumprods(ltree, rtree), ltree, rtree)
def findTarget(tototal, target, tree):
global result
lt, rt = tree[1], tree[2]
# Put smaller-range tree second
if lt[0][1]-lt[0][0] < rt[0][1]-rt[0][0]: lt, rt = rt, lt
(la,ma,aa), (lb,mb,bb) = lt[0], rt[0]
lc, mc = la+lb, ma+mb
count = 0
for j in range(max(lb,tototal-ma), min(mb,tototal-la)+1):
i = tototal-j
count += aa[i] * bb[j]
if count >= target: break
# Suppose that any way of getting i in left tree is ok
if lt[1]: findTarget(i, randx(aa[i]), lt)
else: result += [i]
# Suppose that any way of getting j in right tree is ok
if rt[1]: findTarget(j, randx(bb[j]), rt)
else: result += [j]
spans, ttotal, tries = [(1,6), (5,11), (2,9), (3,7), (4,9), (4,12), (5,8),
(3,5), (2,9), (3,11), (3,9), (4,5), (5,9), (6,13),
(7,8), (4,11)], 100, 10
spans, ttotal, tries = [(10*i,10*i+9) for i in range(16)], 1300, 10000
spans, ttotal, tries = [(1,3) for i in range(32)], 64, 10000
spans, ttotal, tries = [(1,10) for i in range(16)], 100, 10
print 'spans=', spans
vals = [(p, q, [int(i>=p) for i in range(q+1)]) for (p,q) in spans]
tree = maketree(vals)
nways = tree[0][2][ttotal]
print 'nways({}) = {}'.format(ttotal, nways)
for i in range(1,tries):
result = []
findTarget(ttotal, randx(nways), tree)
print '{:2}: {} {}'.format(i, sum(result), result)
在下面显示的输出样本中,带有冒号的行包含样本编号、样本总数和样本值数组。其他行显示了跨度集和获得所需总数的方式的数量。
spans= [(1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10), (1, 10)]
nways(100) = 202752772954792
1: 100 [8, 9, 1, 2, 8, 1, 10, 6, 6, 3, 6, 10, 6, 10, 10, 4]
2: 100 [2, 6, 10, 3, 1, 10, 9, 5, 10, 6, 2, 10, 9, 7, 4, 6]
3: 100 [1, 6, 5, 1, 9, 10, 10, 7, 10, 2, 8, 9, 10, 9, 2, 1]
4: 100 [10, 7, 6, 3, 7, 8, 6, 5, 7, 7, 5, 1, 9, 6, 9, 4]
5: 100 [7, 1, 10, 5, 5, 4, 9, 5, 3, 9, 2, 8, 6, 8, 10, 8]
spans= [(1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3), (1, 3)]
nways(64) = 159114492071763
1: 64 [2, 2, 1, 3, 1, 2, 2, 2, 1, 2, 3, 3, 3, 2, 2, 1, 2, 3, 1, 3, 1, 3, 2, 1, 2, 3, 2, 2, 1, 2, 2, 2]
2: 64 [1, 2, 1, 1, 1, 3, 3, 3, 2, 1, 1, 2, 3, 2, 2, 3, 3, 3, 1, 2, 1, 2, 2, 3, 2, 2, 1, 3, 1, 3, 2, 2]
3: 64 [2, 1, 3, 2, 3, 3, 1, 3, 1, 3, 2, 2, 1, 2, 1, 3, 1, 3, 1, 2, 2, 2, 2, 1, 1, 3, 2, 2, 3, 2, 3, 1]
4: 64 [2, 3, 3, 2, 3, 2, 1, 3, 2, 2, 1, 2, 1, 1, 3, 2, 2, 3, 3, 1, 1, 2, 2, 1, 1, 2, 3, 3, 2, 1, 1, 3]
5: 64 [1, 1, 1, 3, 2, 2, 3, 2, 2, 1, 2, 2, 1, 2, 1, 1, 3, 3, 2, 3, 1, 2, 2, 3, 3, 3, 2, 2, 1, 3, 3, 1]
spans= [(0, 9), (10, 19), (20, 29), (30, 39), (40, 49), (50, 59), (60, 69), (70, 79), (80, 89), (90, 99), (100, 109), (110, 119), (120, 129), (130, 139), (140, 149), (150, 159)]
nways(1323) = 5444285920
1: 1323 [8, 19, 25, 35, 49, 59, 69, 76, 85, 99, 108, 119, 129, 139, 148, 156]
2: 1323 [8, 16, 29, 39, 48, 59, 69, 77, 88, 95, 109, 119, 129, 138, 147, 153]
3: 1323 [9, 16, 28, 39, 49, 58, 69, 79, 87, 96, 106, 115, 128, 138, 149, 157]
4: 1323 [8, 17, 29, 36, 45, 58, 69, 78, 89, 99, 106, 119, 128, 135, 149, 158]
5: 1323 [9, 16, 27, 34, 48, 57, 69, 79, 88, 99, 109, 119, 128, 139, 144, 158]
spans= [(1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (
1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (
1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (1, 30), (
1, 30), (1, 30), (1, 30)]
nways(640) = 19144856039395888221416547336829636235610525
1: 640 [7, 24, 27, 9, 30, 23, 30, 27, 28, 29, 2, 30, 28, 19, 7, 27, 10, 2, 21, 23, 24, 2
7, 24, 16, 29, 8, 13, 23, 2, 19, 27, 25]
2: 640 [30, 2, 17, 28, 30, 16, 5, 1, 26, 24, 22, 19, 26, 16, 16, 30, 27, 15, 19, 30, 15,
30, 22, 5, 30, 9, 13, 25, 19, 15, 30, 28]
3: 640 [2, 24, 1, 23, 20, 5, 30, 22, 24, 19, 22, 9, 28, 29, 5, 24, 14, 30, 24, 16, 26, 2
1, 26, 20, 20, 19, 24, 29, 24, 8, 23, 29]
4: 640 [7, 20, 16, 24, 22, 14, 28, 28, 26, 8, 21, 9, 22, 24, 28, 19, 5, 13, 9, 24, 25, 2
2, 29, 18, 20, 21, 17, 26, 30, 9, 26, 30]