我试图寻找具有1,3,5,7 in的7位数字组合的数量(或更多,实际上需要它工作10,但用7测试更快)它。尝试了一些不同的方法,比如使用
combinations = 0
for combination in itertools.product(xrange(10), repeat=7):
if all(x in combination for x in (1,3,5,7)):
combinations += 1
但是,如果1不在列表中,那么下一个方法的速度大约要快4倍,因为它不会找到3,5,7。
combinations = 0
for combination in itertools.product(xrange(10), repeat=7):
if 1 in combination:
if 3 in combination:
if 5 in combination:
if 7 in combination:
combinations += 1
我确信有更多的切割方法可以通过numpy或类似的东西来实现这个结果,但我无法弄清楚。
感谢您的反馈
答案 0 :(得分:2)
问题是找到包含所有数字1,3,5,7的k位数字。
这个答案包含许多解决方案,其复杂性和算法效率都在提高。最后,我们能够在几分之一秒内计算出巨大k的解,例如10 ^ 12,模数为大素数。
最后一节包含的测试可以很好地证明所有实现都是正确的。
我们将使用这种缓慢的方法来测试更优化的代码版本:
def contains_1357(i):
i = str(i)
return all(x in i for x in '1357')
def combos_slow(k):
return sum(contains_1357(i) for i in xrange(10 ** k))
最简单的中等效率方法是计算。一种方法是计算所有k位数字,其中四个特殊数字的第一次出现在数字a,b,c,d处。
给定a,b,c,d,a之前的数字必须为0,2,4,6,8,9,数字a必须是[1,3,5,7]中的一个, a和b之间的数字必须与数字a或任何安全数字相同,数字b必须是[1,3,5,7]中的一个,与a中的数字不同,等等。
对所有可能的a,b,c,d求和得出结果。像这样:
import itertools
def combos0(k):
S = 0
for a, b, c, d in itertools.combinations(range(k), 4):
S += 6 ** a * 4 * 7**(b-a-1) * 3 * 8**(c-b-1) * 2 * 9**(d-c-1) * 10**(k-d-1)
return S
你可以通过动态编程更有效地解决这个问题:让c [j] [i]是包含(1,3,5,7)正好j个不同数字的i位数字。
然后c满足这些递归关系:
c[0][0] = 1
c[j][0] = 0 for j > 0
c[0][i] = 6 * c[0][i-1] for i > 0
c[j][i] = (6+j)c[j][i-1] + (5-j)c[j-1][i-1] for i, j > 0
复发关系的最后一行是最难理解的。第一部分(6+j)c[j][i-1]
表示您可以从i
包含{{1}的数字编号中创建包含j
数字1,3,5,7的i-1
位数字数字1,3,5,7的数字,并添加一个额外的数字,即0,2,4,6,8,9或您已经获得的任何数字。同样,第二部分j
表示您可以使用(5-j)c[j-1][i-1]
个数字,其中包含数字1,3,5,7的i-1
,并将其设为j-1
- 位数包含i
个特殊数字的数字,通过添加您尚未使用过的数字之一。其中j
。
这导致使用动态编程的这个O(k)解决方案:
5-j
我们可以打印组合(10):
def combos(k):
c = [[0] * (k + 1) for _ in xrange(5)]
c[0][0] = 1
for i in xrange(1, k+1):
c[0][i] = 6 * c[0][i-1]
for j in xrange(1, 5):
c[j][i] = (6 + j) * c[j][i-1] + (5-j) * c[j-1][i-1]
return c[4][k]
这给出了这个输出:
print 'combos(10) =', combos(10)
上面的解决方案已经足够快,可以在几分之一秒内计算combos(10) = 1425878520
。但是,通过观察combos(10000)
的值仅依赖于表中的前一列,可以稍微优化DP解决方案以使用O(1)而不是O(k)空间。稍加注意(为了确保我们在重新使用之前不会覆盖它们),我们可以编写如下代码:
c
最终,通过将递归关系表示为逐个矩阵的乘法,并通过平方使用取幂,可以将结果得到O(log k)时间和O(1)空间。这使得即使对于大量k(此处为组合(10 ^ 12)模2 ^ 31-1)也可以计算def combos2(k):
c = [1, 0, 0, 0, 0]
for _ in xrange(k):
for j in xrange(4, 0, -1):
c[j] = (6+j)*c[j] + (5-j)*c[j-1]
c[0] *= 6
return c[4]
。看起来像这样:
combos(k) modulo X
鉴于你原来的代码在def mat_vec(M, v, X):
return [sum(M[i][j] * v[j] % X for j in xrange(5)) for i in xrange(5)]
def mat_mul(M, N, X):
return [[sum(M[i][j] * N[j][k] for j in xrange(5)) % X for k in xrange(5)] for i in xrange(5)]
def mat_pow(M, k, X):
r = [[i==j for i in xrange(5)] for j in xrange(5)]
while k:
if k % 2:
r = mat_mul(r, M, X)
M = mat_mul(M, M, X)
k //= 2
return r
def combos3(k, X):
M = [[6, 0, 0, 0, 0], [4, 7, 0, 0, 0], [0, 3, 8, 0, 0], [0, 0, 2, 9, 0], [0, 0, 0, 1, 10]]
return mat_vec(mat_pow(M, k, X), [1, 0, 0, 0, 0], X)[4]
print combos3(10**12, (2**31) - 1)
上挣扎,这是一个很大的改进!
我们可以相互测试每个函数(对于小值,可以k=10
)。由于combos_slow
有一个额外的arg,我们将它包装在一个函数中,该函数传递的模数保证大于结果。
combos3
这针对i< 7测试针对def combos3p(k):
return combos3(k, 10**k)
for c in [combos0, combos, combos2, combos3p]:
for i in xrange(40 if c == combos0 else 100):
assert c(i) == (combos_slow if i < 7 else combos)(i)
的所有实现,针对7&lt; = i&lt; 7测试针对彼此的所有实现。 100(效率较低的combos_slow
停止在40)。