# Building a number pyramid

I have been working a number pyramid program. This is a question from Y. Daniel Laing's Introduction to programming using Python (which is based on Python 3.2). The question is from chapter 5, and it is number 5.19 in programming exercises portion of the chapter. The question is stated as follows:

Write a program that prompts the user to enter an integer from 1 to 15 and displays it as the following sample run:

I don't have the image, but it is something like this:

Enter the number of lines: 3

                          1
2  1  2
3  2  1  2  3


The solution I have come up with is this:

p = eval(input("Enter the number of lines: "))

num = 0
y = 1

while num < p:
y = num

line = str(y+1) + "  "

while y >= 1:

y -= 1
line += str(y+1) + "  "

while  y < num:

line += str(y + 2) + "  "

y +=1

print(format(line, "^80s"))

num +=1


If anyone has a better solution that would be simpler, please let me know.

• But that's not a working code at all. It will just get infinitely stuck at the first loop. – Rene Saarsoo Oct 1 '13 at 6:44

## 4 Answers

Like 200_success said, the key is in thinking at the higher level. But I think his approach can be simplified.

Lets first define a row function, which simply calculates a list of numbers in an n'th row of our output:

# When n=3, returns [3, 2, 1, 2, 3]
def row(n):
return list(reversed(range(2, n+1))) + list(range(1, n+1))


And now we can use this to print out the lines:

for n in range(1, 5):
print(row(n))


Giving us the following output:

[1]
[2, 1, 2]
[3, 2, 1, 2, 3]
[4, 3, 2, 1, 2, 3, 4]


The core of the problem is now solved. The only thing left is formatting. For this we can write another function, which formats each number in list into a three-character box (with "%3s"), and also appends a certain amount of padding to make sure lines are aligned like a pyramid:

def formatted_row(n, num_rows):
padding = '   ' * (num_rows-n)
return padding + ''.join(["%3s" % x for x in row(n)])


Now we're almost there. Just need to tie it all together:

# When n=3, returns [3, 2, 1, 2, 3]
def row(n):
return list(reversed(range(2, n+1))) + list(range(1, n+1))

def formatted_row(n, num_rows):
padding = '   ' * (num_rows-n)
return padding + ''.join(["%3s" % x for x in row(n)])

num_lines = eval(input("Enter the number of lines: "))
for n in range(1, num_lines+1):
print(formatted_row(n, num_lines))


Another high-level of thinking about pyramid is as of matrix:

f(1, -n) f(1, -n+1) ... f(1, -1) f(1, 0) f(1, 1) ... f(1, n)
f(2, -n) ...            f(2, -1) f(2, 0) f(2, 1) ... f(2, n)
...                              ...                 ...
f(n, -n) ...                     f(n, 0) ...         f(n, n)


Where some of f(x, y) is numbers and some are just spaces. Note, how I chose to index columns from -n to n instead of from 0, this will make our code simpler. Given all that, it's trivial to write f():

def f(line, place):
# number is just an offset from center + 1
number = abs(place) + 1
# don't display numbers greater than line number
return str(number) if number <= line else ' '


Now we only need to go for all lines from 1 to pyramid height and display a row of f() values joined with ' ':

def pyramid(height):
# same indexes for each line, so we'll reuse them
places = range(-height+1, height)
for line in range(1, height+1):
print ' '.join(f(line, place) for place in places)


As an added bonus we can trivially prevent pyramid from skewing for numbers > 9 by changing last line in f() to:

    return str(number) if number <= line else ' ' * len(str(number))


This feels like a C program. Python can be a lot more compact, and more importantly, expressive. The key is to think at a higher level than a few characters at a time. List comprehensions help a lot.

Each row consists of a countdown, a "1" in the middle, then an ascending count. Let's work on the right half first. We can generate the ascending count using range(). We have to format them all into a fixed width column. Then we concatenate the mirror-image of the right half, the middle "1", and the right half.

def pyramid(height):
FMT = '%3s'
def row(r):
right_half = [FMT % num for num in range(2, 2 + r)] + \
[FMT % ' ' for spc in range(2 + r, 1 + height)]
return ''.join(list(reversed(right_half)) + # Mirrored right half
[FMT % 1] +                  # Center
right_half)                  # Right half
return "\n".join([row(r) for r in range(height)])

print(pyramid(p))

• Wow Half of what you guys just wrote we haven't even touched on, I really think they should reconstruct this book because I am finding that a lot of the higher functions can be used in these exercises and yet they haven't even mentioned them, as for the formatting yeah this book only gives about 5 options. – Jammie Creekmore Oct 3 '13 at 13:54

Agree, the first block definitely puts you in an infinite loop. Also the three while loops don't make it clear what you are doing - better to break up the padding from the pyramid construction.

I'm also working from Yang's book. This challenge in Ch.5 assumes no knowledge of functions but the above got me close to a solution with this:

lines = input("enter the number of lines")

lines = int(lines)

count = 1

while count < lines+1:

padding = '   '*(lines-count)

digits = list(range(1,count+1))

output = padding + str(digits[::-1]) + str(digits[1:])
print(output)
count+=1

• Welcome to Code Review! A good answer should be about the code in the question. Please explain what you did differently and why it is better than the posted code. See also How to Answer – janos Jul 26 '17 at 7:40
• We are looking for answers that provide insightful observations about the code in the question. Answers that consist of independent solutions with no justification do not constitute a code review, and may be removed. – janos Jul 26 '17 at 12:02