I have a recursive function drawing a fractal, using the python turtle module:
def fract(t, order, size):
if order == 0:
t.forward(size)
else:
for angle in (60, -120, 60, 0):
fract(t, order - 1, size / 3)
t.left(angle)
I have another function calling the first function and modifying the last angle, so that the fractals build a circle
def circle(t, order, size):
for i in range(order):
fract(t, 2, size)
t.right(360 / order)
circle(t, 4, 300)
While this works as intended, the real goal is to get the same result in a single recursive function.
Obviously this is not a real programming case, but a task from a python beginner's book, which I'm totally stuck with. I suppose the awkward title of the question reflects my lack of understanding the problem.
I agree with the sentiments of #quamrana on this matter, but let's solve what could be a difficult problem. With a bit of trickery.
First, your consolidated function has to take four argument as the order argument to circle() isn't related to the order argument to fract(). We'll rename the first of these to sides() as that's what it represents.
Second, your fract() function isn't completely recursive, it uses iteration internally. I'm going to follow that same pattern in my solution.
Finally, we need a bit of hidden information to work with -- you could use a defaulted fifth argument that changes internally but instead I'm going to play with the type of sides to achieve this result:
import turtle as t
def fract(t, sides, order, size):
if order == 0:
t.forward(size)
elif sides is not None:
for _ in range(sides):
fract(t, None, order, size)
t.right(360 / sides)
else:
for angle in (60, -120, 60, 0):
fract(t, None, order - 1, size / 3)
t.left(angle)
t.speed('fastest') # because I have no patience
fract(t, 4, 2, 300)
t.hideturtle()
t.exitonclick()
I believe this achieves your desired result with minimal change to your original code. In addtion to the fract(t, 4, 2, 300) invocation, which produces your original figure, we can also do variations like fract(t, 3, 3, 300):
The next problem you might want to tackle is how to center these images on the screen so that fract(t, 5, 1, 300) doesn't fall off the edge.
Assuming that your fract() function is supposed to call itself, then I have been able to run your code successfully.
What you have done is to define a recursive function fract() which is called multiple times by your circle() function. This is called composing your functions. This is a good thing.
Each of your functions has closely defined behaviour, that is they are cohesive. This means that other programmers can pick up your functions (especially fract()) and reuse them in their own programs.
My opinion is that it is best to have many small cohesive functions (and classes and modules) which can be combined in many more ways than were originally intended.
Related
I've made a subdivision code that allows division of a polygon by bounding box method. subdivision(coordinates) results in subblockL and subblockR (left and right). If I want to repeat this subdivision code until it reaches the area less than 200, I would need to use recursion method.
ex:
B = subdivision(A)[0], C = subdivision(B)[0], D = subdivision(C)[0]... until it reaches the area close to 200. (in other words,
subdivision(subdivision(subdivision(A)[0])[0])[0]...)
How can I simplify repetition of subdivision? and How can I apply subdivision to every block instead of single block?
while area(subdivision(A)[0]) < 200:
for i in range(A):
subdivision(i)[0]
def sd_recursion(x):
if x == subdivision(A):
return subdivision(A)
else:
return
I'm not sure what function to put in
"What function to put in" is the function itself; that's the definition of recursion.
def sd_recursive(coordinates):
if area(coordinates) < 200:
return [coordinates]
else:
a, b = subdivision(coordinates)
return sd_recursive(a) + sd_recursive(b) # list combination, not arithmetic addition
To paraphrase, if the area is less than 200, simply return the polygon itself. Otherwise, divide the polygon into two parts, and return ... the result of applying the same logic to each part in turn.
Recursive functions are challenging because recursive functions are challenging. Until you have wrapped your head around this apparently circular argument, things will be hard to understand. The crucial design point is to have a "base case" which does not recurse, which in other words escapes the otherwise infinite loop of the function calling itself under some well-defined condition. (There's also indirect recursion, where X calls Y which calls X which calls Y ...)
If you are still having trouble, look at one of the many questions about debugging recursive functions. For example, Understanding recursion in Python
I assumed the function should return a list in every case, but there are multiple ways to arrange this, just so long as all parts of the code obey the same convention. Which way to prefer also depends on how the coordinates are represented and what's convenient for your intended caller.
(In Python, ['a'] + ['b'] returns ['a', 'b'] so this is not arithmetic addition of two lists, it's just a convenient way to return a single list from combining two other lists one after the other.)
Recursion can always be unrolled; the above can be refactored to
def sd_unrolled(coordinates):
result = []
while coordinates:
if area(coordinates[0]) < 200:
result.extend(coordinates[0])
coordinates = coordinates[1:]
a, b = subdivision(coordinates[0])
coordinates = [a, b] + coordinates[1:]
return result
This is tricky in its own right (but could perhaps be simplified by introducing a few temporary variables) and pretty inefficient or at least inelegant as we keep on copying slices of the coordinates list to maintain the tail while we keep manipulating the head (the first element of the list) by splitting it until each piece is small enough.
I need to draw a shape using recursion and turtle graphics.
I'm more of a looper and rarely ever use recursion, so some help here with this would be nice. Not even sure where to start.
The shape just needs to be cool looking.
Oh man! What a fun problem :)
As you are a self-proclaimed 'looper', I would think about recursion just as you would looping.
In a for loop, you execute the body of the loop until the for loop condition is satisfied. Now, recursion is very similar. You keep calling the function until the parameters of the function no longer hit a recursive case. They hit a base case instead which then returns a value that the recursion can then build up on.
So thinking about recursion this way, let's think about how to draw a square. You need to first identify what parts of the code get repeated (i.e. what would be in the body of a for loop trying to do the same thing). Then, identify when you want this repetition to stop (i.e. how do I know when a for loop exits).
While drawing a square, I can think of two major things that get repeated at least 4 times. The turtle goes forward a certain number of steps and the turtle turns 90 degrees (or 270 degrees depending on orientation). So this would be something we detail in our recursive case.
Now, let's think about the base case. Well, we know that a square has 4 sides, so after the turtle draws four sides, we want it to stop.
Lastly, let's think about the function declaration and how these two pieces, the recursive case and the base case, play into it. A function declaration may take the following form (in Python):
def draw_square_recursive(turn_deg=90, side_len, sides=4):
"""
Function draws a square with turtle graphics recursively
:param turn_deg: an int, the number of degrees a turtle should turn
:param side_len: an int, the length of a side of the square
:param sides: an int, the number of sides in our square
"""
turn_deg and side_len will be important for our recursive case as they define how a turtle should turn and how far it should 'walk'. sides is an interesting parameter and one we can use to dictate whether to continue recurring or stop. If we subtract 1 from sides every time we draw a side, we will know that we need to stop recurring when sides == 0, a base case!
Thus, whenever we call our function to recur again, we will call it as, draw_square_recursive(side_len, sides-1):
Overall, the structure of the function would look like:
def draw_square_recursive(turn_deg=90, side_len, sides=4):
"""
Function draws a square with turtle graphics recursively
:param turn_deg: an int, the number of degrees a turtle should turn
:param side_len: an int, the length of a side of the square
:param sides: an int, the number of sides in our square
"""
if sides == 0:
# base case!
else:
# recursive case!
Note that this function named draw_square_recursive but it can be much more generalized to other shapes. Do you see how?
Sorry if this was a long winded answer! Hope it helps ;p
Without any parametrization, here a beginning:
import time
from turtle import *
def recurse(n):
if n>0:
left(10)
forward(5)
recurse(n-1)
recurse(20)
time.sleep(5)
When you create recursive function, you need to have a stop criterion that effectively guaranties your program will exit at some point.
More editorial than answer, but recursion like this:
def recurse(n):
if n>0:
left(10)
forward(5)
recurse(n-1)
which is better written as iteration:
for n in range(2):
left(10)
forward(5)
is similar to folks who ask, "How can I count the number of elements in a list using recursion?" Ditto drawing a square using recursion.
I understand the goal is to learn about recursion, but what seems to get lost is that there are times where recursion makes wonderful things happen and times where it just slows down your program. Fractals are an opportunity to so something of wonder with recursion:
import sys
from turtle import Turtle, Screen
def hilbert_curve(n, turtle, angle=90):
if n <= 0:
return
turtle.left(angle)
hilbert_curve(n - 1, turtle, -angle)
turtle.forward(1)
turtle.right(angle)
hilbert_curve(n - 1, turtle, angle)
turtle.forward(1)
hilbert_curve(n - 1, turtle, angle)
turtle.right(angle)
turtle.forward(1)
hilbert_curve(n - 1, turtle, -angle)
turtle.left(angle)
depth = int(sys.argv[1])
size = 2 ** depth
screen = Screen()
screen.setworldcoordinates(0, 0, size, size)
yertle = Turtle('turtle')
yertle.speed('fastest')
yertle.penup()
yertle.goto(0.5, 0.5)
yertle.pendown()
hilbert_curve(depth, yertle)
yertle.hideturtle()
screen.exitonclick()
USAGE
% python3 hilbert.py 5
(PARTIAL) OUTPUT
I'm not picking on the other answers, I'm suggesting you think big (or at least beyond "just needs to be cool looking".)
I am trying to solve Euler problem 18 where I am required to find out the maximum total from top to bottom. I am trying to use recursion, but am stuck with this.
I guess I didn't state my problem earlier. What I am trying to achieve by recursion is to find the sum of the maximum number path. I start from the top of the triangle, and then check the condition is 7 + findsum() bigger or 4 + findsum() bigger. findsum() is supposed to find the sum of numbers beneath it. I am storing the sum in variable 'result'
The problem is I don't know the breaking case of this recursion function. I know it should break when it has reached the child elements, but I don't know how to write this logic in the program.
pyramid=[[0,0,0,3,0,0,0,],
[0,0,7,0,4,0,0],
[0,2,0,4,0,6,0],
[8,0,5,0,9,0,3]]
pos=[0,3]
def downleft(pyramid,pos):#returns down left child
try:
return(pyramid[pos[0]+1][pos[1]-1])
except:return(0)
def downright(pyramid,pos):#returns down right child
try:
return(pyramid[pos[0]+1][pos[1]+1])
except:
return(0)
result=0
def find_max(pyramid,pos):
global result
if downleft(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]-1]) > downright(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]+1]):
new_pos=[pos[0]+1,pos[1]-1]
result+=downleft(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]-1])
elif downright(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]+1]) > downleft(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]-1]):
new_pos=[pos[0]+1,pos[1]+1]
result+=downright(pyramid,pos)+find_max(pyramid,[pos[0]+1,pos[1]+1])
else :
return(result)
find_max(pyramid,pos)
A big part of your problem is that you're recursing a lot more than you need to. You should really only ever call find_max twice recursively, and you need some base-case logic to stop after the last row.
Try this code:
def find_max(pyramid, x, y):
if y >= len(pyramid): # base case, we're off the bottom of the pyramid
return 0 # so, return 0 immediately, without recursing
left_value = find_max(pyramid, x - 1, y + 1) # first recursive call
right_value = find_max(pyramid, x + 1, y + 1) # second recursive call
if left_value > right_value:
return left_value + pyramid[y][x]
else:
return right_value + pyramid[y][x]
I changed the call signature to have separate values for the coordinates rather than using a tuple, as this made the indexing much easier to write. Call it with find_max(pyramid, 3, 0), and get rid of the global pos list. I also got rid of the result global (the function returns the result).
This algorithm could benefit greatly from memoization, as on bigger pyramids you'll calculate the values of the lower-middle areas many times. Without memoization, the code may be impractically slow for large pyramid sizes.
Edit: I see that you are having trouble with the logic of the code. So let's have a look at that.
At each position in the tree you want to make a choice of selecting
the path from this point on that has the highest value. So what
you do is, you calculate the score of the left path and the score of
the right path. I see this is something you try in your current code,
only there are some inefficiencies. You calculate everything
twice (first in the if, then in the elif), which is very expensive. You should only calculate the values of the children once.
You ask for the stopping condition. Well, if you reach the bottom of the tree, what is the score of the path starting at this point? It's just the value in the tree. And that is what you should return at that point.
So the structure should look something like this:
function getScoreAt(x, y):
if at the end: return valueInTree(x, y)
valueLeft = getScoreAt(x - 1, y + 1)
valueRight = getScoreAt(x + 1, y + 1)
valueHere = min(valueLeft, valueRight) + valueInTree(x, y)
return valueHere
Extra hint:
Are you aware that in Python negative indices wrap around to the back of the array? So if you do pyramid[pos[0]+1][pos[1]-1] you may actually get to elements like pyramid[1][-1], which is at the other side of the row of the pyramid. What you probably expect is that this raises an error, but it does not.
To fix your problem, you should add explicit bound checks and not rely on try blocks (try blocks for this is also not a nice programming style).
I have to write a collide method inside a Rectangle class that takes another Rectangle object as a parameter and returns True if it collides with the rectangle performing the method and False if it doesn't. My solution was to use a for loop that iterates through every value of x and y in one rectangle to see if it falls within the other, but I suspect there might be more efficient or elegant ways to do it. This is the method (I think all the names are pretty self explanatory, just ask if anything isn't clear):
def collide(self,target):
result = False
for x in range(self.x,self.x+self.width):
if x in range(target.get_x(),target.get_x()+target.get_width()):
result = True
for y in range(self.y,self.y+self.height):
if y in range(target.get_y(),target.get_y()+target.get_height()):
result = True
return result
Thanks in advance!
The problem of collision detection is a well-known one, so I thought rather than speculate I might search for a working algorithm using a well-known search engine. It turns out that good literature on rectangle overlap is less easy to come by than you might think. Before we move on to that, perhaps I can comment on your use of constructs like
if x in range(target.get_x(),target.get_x()+target.get_width()):
It is to Python's credit that such an obvious expression of your idea actually succeeds as intended. What you may not realize is that (in Python 2, anyway) each use of range() creates a list (in Python 3 it creates a generator and iterates over that instead; if you don't know what that means please just accept that it's little better in computational terms). What I suspect you may have meant is
if target.get_x() <= x < target.get_x()+target.get_width():
(I am using open interval testing to reflect your use of range())This has the merit of replacing N equality comparisons with two chained comparisons. By a relatively simple mathematical operation (subtracting target.get_x() from each term in the comparison) we transform this into
if 0 <= x-target.get_x() < target.get_width():
Do not overlook the value of eliminating such redundant method calls, though it's often simpler to save evaluated expressions by assignment for future reference.
Of course, after that scrutiny we have to look with renewed vigor at
for x in range(self.x,self.x+self.width):
This sets a lower and an upper bound on x, and the inequality you wrote has to be false for all values of x. Delving beyond the code into the purpose of the algorithm, however, is worth doing. Because any lit creation the inner test might have done is now duplicated many times over (by the width of the object, to be precise). I take the liberty of paraphrasing
for x in range(self.x,self.x+self.width):
if x in range(target.get_x(),target.get_x()+target.get_width()):
result = True
into pseudocode: "if any x between self.x and self.x+self.width lies between the target's x and the target's x+width, then the objects are colliding". In other words, whether two ranges overlap. But you sure are doing a lot of work to find that out.
Also, just because two objects collide in the x dimension doesn't mean they collide in space. In fact, if they do not also collide in the y dimension then the objects are disjoint, otherwise you would assess these rectangles as colliding:
+----+
| |
| |
+----+
+----+
| |
| |
+----+
So you want to know if they collide in BOTH dimensions, not just one. Ideally one would define a one-dimensional collision detection (which by now we just about have ...) and then apply in both dimensions. I also hope that those accessor functions can be replaced by simple attribute access, and my code is from now on going to assume that's the case.
Having gone this far, it's probably time to take a quick look at the principles in this YouTube video, which makes the geometry relatively clear but doesn't express the formula at all well. It explains the principles quite well as long as you are using the same coordinate system. Basically two objects A and B overlap horizontally if A's left side is between B's left and right sides. They also overlap if B's right is between A's left and right. Both conditions might be true, but in Python you should think about using the keyword or to avoid unnecessary comparisons.
So let's define a one-dimensional overlap function:
def oned_ol(aleft, aright, bleft, bright):
return (aleft <= bright < aright) or (bleft <= aright < bright)
I'm going to cheat and use this for both dimensions, since the inside of my function doesn't know which dimension's data I cam calling it with. If I am correct, the following formulation should do:
def rect_overlap(self, target):
return oned_ol(self.x, self.x+self.width, target.x, target.x+target.width) \
and oned_ol(self.y, self.y+self.height, target.y, target.y+target.height
If you insist on using those accessor methods you will have to re-cast the code to include them. I've done sketchy testing on the 1-D overlap function, and none at all on rect_overlap, so please let me know - caveat lector. Two things emerge.
A superficial examination of code can lead to "optimization" of a hopelessly inefficient algorithm, so sometimes it's better to return to first principles and look more carefully at your algorithm.
If you use expressions as arguments to a function they are available by name inside the function body without the need to make an explicit assignment.
def collide(self, target):
# self left of target?
if x + self.width < target.x:
return False
# self right of target?
if x > target.x + target.width :
return False
# self above target?
if y + self.height < target.y:
return False
# self below target?
if y > target.y + target.height:
return False
return True
Something like that (depends on your coord system, i.e. y positive up or down)
I am modeling something that occurs on a square grid that wraps on itself (i.e., if you walk up past the highest point, you end up at the lowest point, like a cylinder; if you walk to the right, you just hit the boundary). I need to keep track of the location of various agents, the amount of resources at different points, and calculate the direction that agents will be moving in based on certain rules.
What's the best way to model this?
Should I make a class that represents points, which has methods to return neighboring points in each direction? If so, I would probably need to make it hashable so that I can use it as keys for the dictionary that contains the full grid (I assume such grid should be a dictionary?)
Or should I make a class that describes the whole grid and not expose individual points as independent objects?
Or should I just use regular (x, y) tuples and have methods elsewhere that allow to look up neighbors?
A lot of what I'll need to model is not yet clearly defined. Furthermore, I expect the geometry of the surface might possibly change one day (e.g., it could wrap on itself in both directions).
EDIT: One additional question: should I attach the information about the quantity of resources to each Point instance; or should I have a separate class that contains a map of resources indexed by Point?
If you want a hashable Point class without too much work, subclass tuple and add your own neighbor methods.
class Point(tuple):
def r_neighbor(self):
return Point((self[0] + 1, self[1]))
def l_neighbor(self):
[...]
x = Point((10, 11))
print x
print x.r_neighbor()
The tuple constructor wants an iterable, hence the double-parens in Point((10, 11)); if you want to avoid that, you can always override __new__ (overriding __init__ is pointless because tuples are immutable):
def __new__(self, x, y):
return super(Point, self).__new__(self, (x, y))
This might also be the place to apply modular arithmetic -- though that will really depend on what you are doing:
def __new__(self, x, y, gridsize=100):
return super(Point, self).__new__(self, (x % gridsize, y % gridsize))
or to enable arbitrary dimension grids, and go back to using tuples in __new__:
def __new__(self, tup, gridsize=100):
return super(Point, self).__new__(self, (x % gridsize for x in tup))
Regarding your question about resources: since Point is an immutable class, it's a poor place to store information about resources that might change. A defaultdict would be handy; you wouldn't have to initialize it.
from collections import defaultdict
grid = defaultdict(list)
p = Point((10, 13))
grid[(10, 13)] = [2, 3, 4]
print grid[p] # prints [2, 3, 4]
print grid[p.r_neighbor] # no KeyError; prints []
If you want more flexibility, you could use a dict instead of a list in defaultdict; but defaultdict(defaultdict) won't work; you have to create a new defaultdict factory function.
def intdict():
return defaultdict(int)
grid = defaultdict(intdict)
or more concisely
grid = defaultdict(lambda: defaultdict(int))
then
p = Point((10, 13))
grid[(10, 13)]["coins"] = 50
print grid[p]["coins"] # prints 50
print grid[p.r_neighbor]["coins"] # prints 0; again, no KeyError
I need to keep track of the location
of various agents, the amount of
resources at different points, and
calculate the direction that agents
will be moving in based on certain
rules.
Sounds like a graph to to me, though I try to see a graph in every problem. All the operations you mentioned (move around, store resources, find out where to move to) are very common on graphs. You would also be able to easily change the topology, from a cylinder to a torus or in any other way.
The only issue is that it takes more space than other representations.
On the plus side you can use a graph library to create the graph and probably even some graph algorithms to calculate where agents go.