简体   繁体   中英

Conversion of iterative algorithm to recursive

Hi I am trying to convert my iterative algorithm to recursive solution to achieve Dynamic Programming after it's done (Do suggest me other ways to reduce time complexity of this triple nested iteration). I am not good with recursion. I had tried to convert it but it is giving me index out of range errors.

Iterative Approach:

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

Recursive Approach:

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

The input should be like L=['1','2','3']

This is the fastest version I can come up with:

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

This should be significantly faster (1/7th the time in my test of lists with 100 elements).

The algorithm is essentially to build a directed graph where the nodes are the numbers in the list. Edges go from node A to node B iff the integer values of those nodes are different and A divides evenly into B.

Then traverse the graph. For each starting node A, find all the nodes B where there's an edge from A to B. On paper, we would then go to all the next nodes C, but we don't need to... we can just count how many edges are leaving node B and add that to our total.

EDIT

Depending on the distribution of values in the list, this is probably faster:

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

Here, you can think of nodes as having a numeric value and a count. This avoids duplicate nodes for duplicate values in the input. Then we basically do the same thing but multiply by the appropriate count at each step.

EDIT 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])

Slight improvement thanks to @Blckknght. Sorting the unique values first saves some time in enumeration.

EDIT 3

See comments, but the original code in this question was actually wrong. Here's code that (I think) does the right thing based on the problem description which can be found in the comments:

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

EDIT 4

Much faster version of the previous code:

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

EDIT 5

More compact version (seems to run in about the same amount of time):

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

Here's how I'd solve your problem. It should use O(N**2) time.

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

My algorithm is very similar to the one smarx is using in his answer, but I keep a count of the number of edges incident to a given value rather than a list.

As far as speeding things up goes (instead of going recursive), testing with 1000 entries, slicing the sorted list at each level cut time by more than half for me (gets rid of numbers less than y, z at their respective levels:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM