您好我正在尝试将迭代算法转换为递归解决方案,以便在完成后实现动态编程(建议我采用其他方法来减少此三重嵌套迭代的时间复杂度)。我不善于递归。我试图转换它,但它给了我超出范围错误的索引。
迭代方法:
def foo(L):
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L):
if x != y:
if (y%x ==0):
for k,z in enumerate(L):
if y != z:
if (z%y==0) and (y%x==0):
A= A+1
return A
递归方法:
A =i =j= k =0 #Initializing globals
def foo(L):
L=sorted(L)
global A ,i,j,k
x=y=z=L
luckyOne(x,y,z)
return A
def luckyOne(x,y,z):
global i,j,k
while(i< len(x) and j < len(y) and k < len(z)):
while(x[i] != y[j]):
luckyTwo(x[i:],y[j:],z[k:])
i+=1
luckyOne(x[i:],y[j:],z[k:])
# i+=1
# return A
i+=1
luckyOne(x[i:],y[j:],z[k:])
return 0
def luckyTwo(x,y,z):
global i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while(y[j]%x[i]==0):
luckyThree(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
return 0
def luckyThree(x,y,z):
global A ,i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while (y[j]!=z[k]):
while((z[k]%y[j]==0) and (y[j]%x[i]==0)):
A+=1
print 'idr aya'
k+=1
luckyThree(x[i:],y[j:],z[k:])
k+=1
luckyThree(x[i:],y[j:],z[k:])
return 0
输入应该像L = [&#39; 1&#39;,&#39; 2&#39;,&#39; 3&#39;]
答案 0 :(得分:3)
这是我能提出的最快的版本:
def foo(lst):
edges = {x: [y for y in lst if x != y and y % x == 0] for x in set(lst)}
return sum(len(edges[y]) for x in lst for y in edges[x])
这应该明显更快(在我测试包含100个元素的列表时的1/7)。
该算法主要是构建有向图,其中节点是列表中的数字。如果这些节点的整数值不同并且A均匀地划分为B,则边缘从节点A转到节点B.
然后遍历图表。对于每个起始节点A,找到所有节点B,其中有从A到B的边缘。在纸面上,我们将转到所有下一个节点C,但我们不需要...我们可以计算多少边缘离开节点B并将其添加到我们的总数中。
修改强>
根据列表中值的分布,这可能更快:
def foo(lst):
counts = Counter(lst)
edges = {x: [y for y in counts if x != y and y % x == 0] for x in counts}
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
在这里,您可以将节点视为具有数值和计数。这避免了输入中重复值的重复节点。然后我们基本上做同样的事情,但在每一步都乘以适当的计数。
编辑2
def foo(lst):
counts = collections.Counter(lst)
edges = collections.defaultdict(list)
for x, y in itertools.combinations(sorted(counts), 2):
if y % x == 0:
edges[x].append(y)
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
@Blckknght带来了轻微的改善。首先对唯一值进行排序可以节省一些枚举时间。
编辑3
请参阅评论,但此问题中的原始代码实际上是错误的。这里的代码(我认为)根据问题描述做了正确的事情,可以在评论中找到:
def foo3(lst):
count = 0
for x, y, z in itertools.combinations(lst, 3):
if y % x == 0 and z % y == 0:
count += 1
return count
print(foo3([1, 2, 3, 4, 5, 6])) # 3
print(foo3([6, 5, 4, 3, 2, 1])) # 0
编辑4
很多以前代码的更快版本:
def foo4(lst):
edges = [[] for _ in range(len(lst))]
for i, j in itertools.combinations(range(len(lst)), 2):
if lst[j] % lst[i] == 0:
edges[i].append(j)
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
编辑5
更紧凑的版本(似乎在大约相同的时间内运行):
def foo5(lst):
edges = [[j for j in range(i + 1, len(lst)) if lst[j] % lst[i] == 0] for i in range(len(lst))]
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
答案 1 :(得分:3)
以下是我如何解决您的问题。它应该使用O(N**2)
时间。
def count_triple_multiples(lst):
count = collections.Counter(lst)
double_count = collections.defaultdict(int)
for x, y in itertools.combinations(sorted(count), 2):
if y % x == 0:
double_count[y] += count[x] * count[y]
triple_count = 0
for x, y in itertools.combinations(sorted(double_count), 2):
if y % x == 0:
triple_count += double_count[x] * count[y]
return triple_count
我的算法非常类似于smarx在他的回答中使用的算法,但是我保留了一个事件的数量,而不是列表。
答案 2 :(得分:0)
对于加速进行(而不是递归),使用1000个条目进行测试,在每个级别对排序列表进行切片,为我减少了一半以上的时间(删除小于y, z
的数字他们各自的水平:
def foo(L):
assert 0 not in L
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L[i + 1:]):
if x != y and not y % x:
for k,z in enumerate(L[i + j + 2:]):
if y != z and not z % y:
A = A + 1
return A