迭代算法转换为递归算法

时间:2017-06-28 20:51:46

标签: python algorithm python-2.7 recursion iteration

您好我正在尝试将迭代算法转换为递归解决方案,以便在完成后实现动态编程(建议我采用其他方法来减少此三重嵌套迭代的时间复杂度)。我不善于递归。我试图转换它,但它给了我超出范围错误的索引。

迭代方法:

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;]

3 个答案:

答案 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