Clover
Clover

Reputation: 393

Repeating triangle pattern in Python

I need to make a triangle of triangle pattern of * depending on the integer input.

For example:

n = 2

    *
   ***
 *  *  *
*********

n = 3

            *
           ***
          *****
       *    *    *
      ***  ***  ***
     ***************
  *    *    *    *    *
 ***  ***  ***  ***  ***
*************************

I've already figured out the code for a single triangle, but I don't know how to duplicate them so they'll appear like a triangle of triangles.

Here's my code for one triangle:

rows = int(input())

for i in range(rows):
    for j in range(i, rows):
        print(" ", end="")
    for j in range(i):
        print("*", end="")
    for j in range(i + 1):
        print("*", end="")
    print()

Upvotes: 25

Views: 6094

Answers (6)

Seb
Seb

Reputation: 4585

Lots of interesting answers already, but I thought I'd add one that lets Python handle the string centering.

def print_fractal(n, char='*'):
    # Width of single triangle
    base = 2*n - 1
    
    # Width of overall figure
    width = base**2
    
    # Lines containing single triangle padded to rectangle of width `base`
    lines = [f'{(2*line + 1)*char:^{base}}' for line in range(n)]
    
    for row in range(n):
        # Print (2*row + 1) triangle blocks next to each other
        for line in lines:
            print(f'{(2*row + 1)*line:^{width}}')
>>> print_fractal(3)
            *            
           ***           
          *****          
       *    *    *       
      ***  ***  ***      
     ***************     
  *    *    *    *    *  
 ***  ***  ***  ***  *** 
*************************

A recursive solution also suggests itself, thanks to inspiration from @Lynn's answer:

def make_fractal(n, depth, block=['*']):
    if not depth:
        return block
    
    width = (2*n - 1)*max(map(len, block))
    
    lines = []
    
    for row in range(n):
        for line in block:
            lines.append(f'{(2*row + 1)*line:^{width}}')
    
    return make_fractal(n, depth - 1, lines)
>>> for line in make_fractal(3, 2): print(line)
            *            
           ***           
          *****          
       *    *    *       
      ***  ***  ***      
     ***************     
  *    *    *    *    *  
 ***  ***  ***  ***  *** 
*************************
>>> for line in make_fractal(2, 3): print(line)
             *             
            ***            
          *  *  *          
         *********         
    *        *        *    
   ***      ***      ***   
 *  *  *  *  *  *  *  *  * 
***************************
>>> for line in make_fractal(2, 2, [' . ', '---']): print(line)
             .             
            ---            
          .  .  .          
         ---------         
    .        .        .    
   ---      ---      ---   
 .  .  .  .  .  .  .  .  . 
---------------------------

Upvotes: 1

Ajax1234
Ajax1234

Reputation: 71471

Using a helper function to build the sub-triangles:

def tri(n):
   r = [(s:=(' '*(((2*n-1)-(2*i-1))//2)))+('*'*(2*i-1))+s for i in range(1, n+1)]
   return r

def triangle(n):
   v = [''.join(j) for i in range(n+1) for j in zip(*[tri(n) for _ in range(2*i-1)])]
   return '\n'.join((s:=' '*((len(v[-1]) - len(i))//2))+i+s for i in v) 

for i in range(1, 4):
   print(triangle(i))
   print('-'*25)
*
-------------------------
    *    
   ***   
 *  *  * 
*********
-------------------------
            *            
           ***           
          *****          
       *    *    *       
      ***  ***  ***      
     ***************     
  *    *    *    *    *  
 ***  ***  ***  ***  *** 
*************************
-------------------------

Upvotes: 2

Troll
Troll

Reputation: 1925

Code

I have simplified the following code so it should now look more clear easy to understand than it used to be.

n = int(input("n = "))

rows = n ** 2
base = n * 2 - 1

for row in range(rows):
    triangle = row // n
    level = row % n

    a_space = " " * (n - triangle - 1) * base
    b_space = " " * (n - level - 1)

    line = (a_space + (b_space + "*" * (level * 2 + 1) + b_space)
        * (triangle * 2 + 1)).rstrip()

    print(line)

How it works

This approach prints out everything in one pass. In the following explanation, I will break down the components of the code, what these variables mean, and how they work.

The height

The first variable that I should mention is n, the number of levels, or the height of every triangle. It is also the number of triangles stacked on top of each other.

Rows

The second variable is rows, the number of rows in the output. We can see a pattern that the number of rows is equal to n squared.

Rows

Base

The next variable is base. It is the number of asterisks at the bottom level of the triangle. It also follows a pattern of odd numbers as we may have noticed.

Base

After that, our loop begins, iterates through every row, and prints out the result.

Triangle and level

These variables tell us which triangle and level of the triangle we are currently at. You can try it yourself by printing out triangle and level every iteration.

for row in range(rows):
    triangle = row // n
    level = row % n

    print(triangle, level)

Triangle and level

The two types of space

The next part is the indentation. Just to give you a brief idea of what a_space and b_space are, here is a visual representation that describes the spaces.

Space

a_space shrinks after every triangle, and b_space shrinks after every level.

a_space = " " * (n - triangle - 1) * base
b_space = " " * (n - level - 1)

We multiply a_space by base because one a_space has a length equal to the base.

Printing out the line

Let's look at it step-by-step.

  1. First, we start our line with a_space.
a_space
  1. Then, we print out the asterisks. The number of asterisks follows the pattern of odd numbers.
"*" * (level * 2 + 1)
  1. We open and close the asterisks with b_space.
b_space + "*" * (level * 2 + 1) + b_space
  1. Then, we simply multiply the whole thing by the number of triangles stacked next to each other horizontally.
(b_space + "*" * (level * 2 + 1) + b_space) * (triangle * 2 + 1)

Putting everything together, we get the line, right-strip it, and print it out.

line = (a_space + (b_space + "*" * (level * 2 + 1) + b_space)
        * (triangle * 2 + 1)).rstrip()

print(line)

Output

Let's try it out with a few test cases.

n = 1

*

n = 2

    *
   ***
 *  *  *
*********

n = 3

            *
           ***
          *****
       *    *    *
      ***  ***  ***
     ***************
  *    *    *    *    *
 ***  ***  ***  ***  ***
*************************

n = 4

                        *
                       ***
                      *****
                     *******
                 *      *      *
                ***    ***    ***
               *****  *****  *****
              *********************
          *      *      *      *      *
         ***    ***    ***    ***    ***
        *****  *****  *****  *****  *****
       ***********************************
   *      *      *      *      *      *      *
  ***    ***    ***    ***    ***    ***    ***
 *****  *****  *****  *****  *****  *****  *****
*************************************************

Upvotes: 55

lynn
lynn

Reputation: 10804

Here is what I think this exercise is trying to teach you.

Let's take your triangle code (there is nothing fundamentally wrong with it):

size = int(input())

for i in range(size):
    for j in range(i, size):
        print(" ", end = "")
    for j in range(i):
        print("*", end = "")
    for j in range(i + 1):
        print("*", end = "")
    print()

and turn it into a function that returns a list of rows/lines:

def triangle(size):
    rows = []
    for i in range(size):
        row = []
        for j in range(i + 1, size):
            row.append(" ")
        for j in range(2 * i + 1):    # i + (i+1) = 2*i+1.
            row.append("*")
        for j in range(i + 1, size):  # Let's add spaces at the end, too.
            row.append(" ")           # It'll make sense in a moment!
        rows.append("".join(row))
    return rows

for row in triangle(5):
    print(row)

#     *
#    ***
#   *****
#  *******
# *********

Now we can tweak it a little bit to work with any rectangular list-of-strings "building block" instead of *:

def triangle(size, block):
    rows = []
    for i in range(size):
        strip = [[] for _ in block]
        for j in range(i + 1, size):
            for s, b in zip(strip, block):
                s.append(" " * len(b))     # Space as wide as the building block
        for j in range(2 * i + 1):
            for s, b in zip(strip, block):
                s.append(b)
        for j in range(i + 1, size):
            for s, b in zip(strip, block):
                s.append(" " * len(b))
        for s in strip:
            rows.append("".join(s))
    return rows


# Make a triangle out of ["abc",
#                         "def"]:

for row in triangle(3, ["abc", "def"]):
    print(row)

#       abc
#       def
#    abcabcabc
#    defdefdef
# abcabcabcabcabc
# defdefdefdefdef

This is somewhat tricky code! Try adding some print() statements to this code to see how it works. zip() loops over two lists in parallel. Actually, you should try rewriting this function yourself from scratch.

The payoff is very satisfying: 🙂

fractal = triangle(4, triangle(4, ["*"]))

for row in fractal:
    print(row)

#                         *
#                        ***
#                       *****
#                      *******
#                  *      *      *
#                 ***    ***    ***
#                *****  *****  *****
#               *********************
#           *      *      *      *      *
#          ***    ***    ***    ***    ***
#         *****  *****  *****  *****  *****
#        ***********************************
#    *      *      *      *      *      *      *
#   ***    ***    ***    ***    ***    ***    ***
#  *****  *****  *****  *****  *****  *****  *****
# *************************************************

You can of course also now create a "triangle of triangles of triangles":

for row in triangle(2, triangle(2, triangle(2, ["()"]))):
    print(row)

#                           ()
#                         ()()()
#                     ()    ()    ()
#                   ()()()()()()()()()
#         ()                ()                ()
#       ()()()            ()()()            ()()()
#   ()    ()    ()    ()    ()    ()    ()    ()    ()
# ()()()()()()()()()()()()()()()()()()()()()()()()()()()

The lesson here is that of generalizing a problem: on the face of it, it's hard to make a triangle of triangles (of triangles). But it's relatively easy to make a triangle out of whatever.

If we write some code that can make triangles out of anything (like our abc def example), we can use that code to make a triangle out of triangles.

Unlike the other answers, we don't need separate logic for the two layers of "triangle" at play here.

Upvotes: 18

Vens8
Vens8

Reputation: 85

The program you made to print a single triangle is not helpful in this case as that would mean you have to print these triangles side by side aligned on stdout. It can only be printed line by line, and it's fairly simple to do so. Here's a naive approach not involving any serious mathematics.

rows = int(input())
p = 2 * rows *(rows - 1)  # Variable to print the left most spaces
x = " "
for i in range(rows):  # For every row
    space = 2 * (rows - 1)  # Space variable to print spaces after an asterisk when needed
    for j in range(rows):  # For every row inside the row
        print(p * x, end="")  # print the left most spaces
        for k in range(2*(i + 1) - 1): 
            print((2*(j + 1) - 1) * "*", end = "")  # Print the asterisk(s) based on the row number
            print(space * x, end = "")  # Print spaces(s) after the asterisk(s)
        space -= 2  # Decrement the number of spaces to be printed after an asterisk for every row
        p -= 1  # Every row has one lesser space on the left
        print()  # Print a new line after every row
    p -= (rows - 1)  # Update the number of spaces to be printed on the left most side for every outer row

Go through the comments above for details. The outputs:

n = 4

                        *      
                       ***    
                      *****  
                     *******
                 *      *      *      
                ***    ***    ***    
               *****  *****  *****  
              *********************
          *      *      *      *      *      
         ***    ***    ***    ***    ***    
        *****  *****  *****  *****  *****  
       ***********************************
   *      *      *      *      *      *      *      
  ***    ***    ***    ***    ***    ***    ***    
 *****  *****  *****  *****  *****  *****  *****  
*************************************************

n = 5

                                        *        
                                       ***      
                                      *****    
                                     *******  
                                    *********
                               *        *        *        
                              ***      ***      ***      
                             *****    *****    *****    
                            *******  *******  *******  
                           ***************************
                      *        *        *        *        *        
                     ***      ***      ***      ***      ***
                    *****    *****    *****    *****    *****
                   *******  *******  *******  *******  *******
                  *********************************************
             *        *        *        *        *        *        *
            ***      ***      ***      ***      ***      ***      ***
           *****    *****    *****    *****    *****    *****    *****
          *******  *******  *******  *******  *******  *******  *******
         ***************************************************************
    *        *        *        *        *        *        *        *        *
   ***      ***      ***      ***      ***      ***      ***      ***      ***
  *****    *****    *****    *****    *****    *****    *****    *****    *****
 *******  *******  *******  *******  *******  *******  *******  *******  *******
*********************************************************************************

And so on. As mentioned already, this is a naive approach for intuition.

Upvotes: 4

Valentino
Valentino

Reputation: 7361

Just another alternative with a function to draw the inner triangle and a main function to print the final result

import sys

n = int(sys.argv[1])

def drawtriangle(num_lines):
    # prepares the inner triagle in a list and return it together with its width (size).
    size = (2*num_lines)-1
    triangle = []
    for i in range(num_lines):
        white_side = num_lines - i - 1
        asterisks = 2*i + 1
        triangle.append(" "*white_side + "*"*asterisks + " "*white_side)
    return triangle, size

def main(num_lines):
    tr, tr_size = drawtriangle(num_lines)

    for j in range(num_lines):
        for line in tr:
            white_triangles = n - j - 1
            white_size = tr_size * white_triangles
            line_repeat = (2*j) + 1
            print(" "*white_size + line*line_repeat + " "*white_size)

main(n)

Output:

n = 1

*

n = 2

    *
   ***
 *  *  *
*********

n = 3

            *
           ***
          *****
       *    *    *
      ***  ***  ***
     ***************
  *    *    *    *    *
 ***  ***  ***  ***  ***
*************************

n = 4

                        *
                       ***
                      *****
                     *******
                 *      *      *
                ***    ***    ***
               *****  *****  *****
              *********************
          *      *      *      *      *
         ***    ***    ***    ***    ***
        *****  *****  *****  *****  *****
       ***********************************
   *      *      *      *      *      *      *
  ***    ***    ***    ***    ***    ***    ***
 *****  *****  *****  *****  *****  *****  *****
*************************************************

Upvotes: 3

Related Questions