Recursive Function For Tracing Deletable Primes - Python 3

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-11 23:08:01

问题


A deletable prime is a prime number that can have its digits removed in a certain order to always create primes, and eventually end up with a single digit prime. For example, 3301 is a deletable prime because it can be manipulated like this: 3301 -> 331 -> 31 -> 3. I'm trying to write a recursive function in Python that traces the all paths of a deletable prime back to its single digit root(s) but I'm completely stuck. It has gotten way over my head and I can't even follow my own code anymore. A complete example of the desired output for the function is something along the lines of getTraces(3793) returns [[3793, 373, 73, 7], [3793, 373, 73, 3], [3793, 373, 37, 7], [3793, 373, 37, 3], [3793, 379, 79, 7], [3793, 379, 79, 3], [3793, 379, 37, 7], [3793, 379, 37, 3]]

Here is the current version of the function I have (if this of any help). I'm trying to get it to return a list of all the possible paths to a single digit prime but I can't wrap my head around the recursive thinking necessary to have it trace all the paths and put them neatly in a list:

def getTraces(prime):
    paths = []
    print("Parents: " + str(asc(prime)))
    for p in asc(prime):
        if len(str(p)) == 1:
            print("One Digit Prime: "+ str(p))
            return [p]
        else:
            traceArray = getTraces(p)
            print("Trace: " + str(traceArray))
            if traceArray != []:
                for t in traceArray:
                    paths.append(str(prime) + ", " + str(p) + ", " + str(t))
                #traceString = str(p) + ", " + str(traceArray)
    return paths

This function just returns a list of all the parents of a prime (all the possible primes with one digit removed) and is used in the getTraces() function

def asc(prime):
    adj = []
    if(isPrime(prime)):
        s = str(prime)
        if len(s) == 1: return []
        for i in range(0, len(s)):
            num = int(s[:i] + s[i+1:])
            if isPrime(num):
                if not num in adj:
                    adj.append(num)
    else:
        print("That is not a prime number!")
        return None
    return adj

And a very nice utility method I made for checking if a number is prime:

def isPrime(x):
    if x <= 1: return False
    for i in range(1, int(sqrt(x)) + 1):
        if not gcd(i, x) == 1:
            #print(i)
            return False
    return True

回答1:


import math

def test(x, curr, acc):
    if x < 10:
        acc.append(curr)
        return
    for i in range(int(math.log(x,10))+1,0,-1):
        k = int(x / (math.pow(10,i))) * math.pow(10,i-1) + (x % math.pow(10,i-1))
        if isPrime(k):
            next = list(curr)
            next.append(int(k))
            test(k, next, acc)

Something like this should work with k being the accumulator. You could add another function to make calling it easier:

def getTraces(prime):
    traces = []
    test(prime, [prime], traces)
    return traces



回答2:


I'm not going to debug your code for you, but I'll try to answer the concrete questions you have:

but I can't wrap my head around the recursive thinking necessary to have it trace all the paths and put them neatly in a list:

To make a recursive function collect its results into a list, you'll need to do something like this:

def _myfn(data, res):
    if (..recurse..):
        return _myfn(data2, res + [item])  # item being the result of this iteration
    else:
        # terminal case
        return res

def myfn(data):
    return _myfn(data, [])

You can squash these into one function:

def myfn(data, res=None):
    if res is None:
        res = []
    if (..recurse..):
        return myfn(data2, res + [item])
    else:
        # terminal case
        return res

or you can make _myfn local to myfn:

def myfn(data):
    def _myfn(data, res):
        if (..recurse..):
            return _myfn(data2, res + [item])
        else:
            # terminal case
            return res
    return _myfn(data, [])

When it comes to wrapping your head around recursive thinking, just remember to (1) always handle the base case ;-) and (2) in each recursion first (i) generate all possible next-steps and (ii) then recurse on each of them. Writing a separate function for (i) usually clears up the code a bit.

As an example of a recursive function that finds all paths from the root to a leaf in a binary tree (slightly pedagogically coded) -- which is what you're trying to do..:

# tree = (root, left, right)

def all_paths(t):
    leaf_paths = []

    def _all_paths(t, res):
        root, left, right = t

        # calculate all next-steps
        next_steps = [step for step in [left, right] if step]

        if not next_steps:
            # handle base case (found one solution)
            leaf_paths.append(res + [root])
        else:
            # recurse
            for step in next_steps:
                _all_paths(step, res + [root])

    _all_paths(t, [])

    return leaf_paths

>>> all_paths( (2, (), ()) )
[[2]]
>>> all_paths(
...  (2,
...    (3, (), ()),
...    (4, (), ())))
[[2, 3], [2, 4]]

and for this graph

>>> t = (
    2,
    (7,
     (2, (), ()),
     (6,
      (5, (), ()),
      (11, (), ()))),
     (5,
      (),
      (9,
       (4, (), ()),
       ())))
>>> print all_paths(t)
[[2, 7, 2], [2, 7, 6, 5], [2, 7, 6, 11], [2, 5, 9, 4]]



回答3:


I was finally able to solve my problem so I thought I might as well post my answer since I can't delete the question. I eventually gave up on trying to do it recursively since I came up with a way to do it without recursion, but I appreciate the help and pointers everyone provided. Here is my solution:

def trace(prime):
    def sub(paths):
        result = []
        """Increases path depth by one"""
        for path in paths:
            pathResult = []
            for parentOfLast in asc(path[len(path)-1]):
                pathResult.append(path + [parentOfLast])
            #print("Path: " + str(pathResult))
            result += (pathResult)
        return result
    result = [[prime]]
    while len(result[0]) < len(str(prime)):
        result = sub(result)
    return result



回答4:


Looks like you find your solution, but given yours definition there is prime numbers that can never be deletables, like 11 or 911 or 1601 because no matter what I do, never reach a single digit prime, and I don't see in yours code that consideration so here is my take on that using recursion and itertools:

import itertools

def isDeletablePrime(n,*,valor=True):
    if isPrime(n):
        N = str(n)
        S = len(N)
        if S>1 and any( p in N for p in "2 3 5 7".split()) :
            resul = list()
            for num in set( map(lambda x: int("".join(x)), itertools.combinations(N,S-1)) ): #set(...) eliminate potencial duplicates like with 331 there are 2 way to get 31, removing the firts or second 3
                temp = isDeletablePrime(num,valor=True)
                if temp:
                    resul.extend( (n,)+tt for tt in temp )
            if valor:
                return tuple(filter(lambda r:len(r)==S, resul ))
            else:
                return any( len(r)==S for r in resul )
        elif n in {2,3,5,7}: #base case
            return ((n,),) if valor else True
    return tuple() if valor else False #base case and default

print(3301,"->", isDeletablePrime(3301) ) 
print(3793,"->", isDeletablePrime(3793) )
print(1601,"->", isDeletablePrime(1601) )

this can be used as a predicate and a function I make it that way for no particular reason



来源:https://stackoverflow.com/questions/33505735/recursive-function-for-tracing-deletable-primes-python-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!