问题
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