Recursive Data into an inverse hill

list = input("Enter data:")

k = [*map(int,list.split())]
l=[k]
while len(l[-1])>1:
    k=[k[i]-k[i-1]for i in range(1,len(k))]
    l+=[k]
    print(*k)

INPUT: 10 20 30 40 50

OUTPUT:

10 10 10 10
0 0 0
0 0
0

I want the output to be in an inverse hill or triangle just like:

10 10 10 10
  0  0  0
    0  0
      0

You can modify or overhaul my code.


Solution 1:

You can use zip to obtain the differences between items of a list and their successor. If you're only printing as you go, you don't need to store the differences in a separate list

To get a good alignment, you would need to control the printing width of each number and the width of the spacing between them. In that case it would be easier to use a fixed width corresponding to the largest number (and use it as spacing as well):

k = [8,13,21,34,55,89]
width  = len(str(max(k)))               # fixed width numbers/spacing
for i in range(len(k)-1):               # i will provide indentation
    k  = [b-a for a,b in zip(k,k[1:])]  # update with differences
    fw = (f"{n:<{2*width}}" for n in k) # fixed width numbers 
    print(" "*width*i, *fw, sep="")     # print with indentation

5   8   13  21  34  
  3   5   8   13  
    2   3   5   
      1   2   
        1

To include the initial content of k in the output, you simply need to loop one more time and move the update of k to after printing it:

k = [8,13,21,34,55,89]
width  = len(str(max(k)))               # fixed width numbers/spacing
for i in range(len(k)):                 # i will provide indentation
    fw = (f"{n:<{2*width}}" for n in k) # fixed width numbers 
    print(" "*width*i, *fw, sep="")     # print with indentation
    k  = [b-a for a,b in zip(k,k[1:])]  # update with differences

8   13  21  34  55  89  
  5   8   13  21  34  
    3   5   8   13  
      2   3   5   
        1   2   
          1   

If it is possible to get negative differences, you should make the width be at least 2 (or +1) to have room for the minus signs

If you're not comfortable with format strings, str(n).ljust(2*width) would do the same thing as f"{n:<{2*width}}"

[EDIT] Using fixed widths is easier but it does waste a bit of horizontal space. If you want to reduce the horizontal space consumption while avoiding ambiguous overlapping of numbers, you will need to process the numbers in two steps: 1) build the list of values in a 2D matrix with values on odd lines placed in odd columns and even lines on even columns. 2) Concatenate the values on each line ensuring every column has the same (largest) width.

k = [8,13,21,34,55,89]

lines = [['']*len(k)*2 for _ in k[1:]]    # prepare with enough columns
for i in range(len(k)-1):                 # i will be indent 
    k    = [b-a for a,b in zip(k,k[1:])]  # update with differences
    lines[i][i:i+len(k)*2:2] = map(str,k) # numbers at odd/even columns

widths = [max(map(len,c)) for c in zip(*lines)] # largest column widths

for line in lines: # print with adjusted widths
    print( "".join(s.ljust(w) for s,w in zip(line,widths)) )

5 8 13 21  34
 3 5  8  13  
  2 3  5     
   1  2      
    1 

The lines matrix has the values organized in the appropriate column to produce an inverted pyramid. By ensuring a consistent width on each column, the amount of horizontal space is optimized and the layout meets the requirement:

['5', '',  '8', '',  '13', '',  '21', '',   '34', '', '', '']
['',  '3', '',  '5', '',   '8', '',   '13', '',   '', '', '']
['',  '',  '2', '',  '3',  '',  '5',  '',   '',   '', '', '']
['',  '',  '',  '1', '',   '2', '',   '',   '',   '', '', '']
['',  '',  '',  '',  '1',  '',  '',   '',   '',   '', '', '']

If you want the initial input to be part of the reversed hill:

k = [8,13,21,34,55,89]

lines = [['']*len(k)*2 for _ in k]        # prepare with enough columns
for i in range(len(k)):                 # i will be indent 
    lines[i][i:i+len(k)*2:2] = map(str,k) # numbers at odd/even columns
    k    = [b-a for a,b in zip(k,k[1:])]  # update with differences

widths = [max(map(len,c)) for c in zip(*lines)] # largest column widths

for line in lines: # print with adjusted widths
    print( "".join(s.ljust(w) for s,w in zip(line,widths)) )

8 13 21  34  55  89
 5  8  13  21  34  
  3  5   8   13    
    2  3   5       
     1   2         
       1          

Solution 2:

I slightly modified your print statement:

k = [*map(int,list.split())]
l=[k]
spaces = 0
while len(l[-1])>1:
    k=[k[i]-k[i-1]for i in range(1,len(k))]
    l+=[k]
    print(' '.join(map(str, k)).center(len(k)*2-1+spaces))
    spaces += 2

Output:

1 1 1 1
 0 0 0 
  0 0  
   0