Pascals Dreieck
Hintergrund
Piko hat die Aufgabe gestellt, ein (Python-)Programm zu schreiben, das das Pascalsche Dreieck nett formatiert ausgibt.
Meine Interpretation
Ich hab meine eigene Idee, was “nett formatiert” bedeutet:
- Keine überflüssigen Leerzeichen in der letzten Zeile.
- Zahlen mit ungradzahliger Anzahl von Ziffern sind genau mittig unterhalb oder oberhalb anderer Zahlen mit ungradzahliger Anzahl von Ziffern, die zwei, vier, sechs, … Zeilen drüber oder drunter auftauchen.
- Zahlen mit gradzahliger Anzahl von Ziffern müssen irgendwie reingefudelt werden. Sagen wir mal: Wenn einige Zeilen tiefer eine Zahl mit einer Ziffer mehr ist, soll die gradziffrige Zahl rechtsbündig drüber stehen. Bug: Das klappt so nicht ganz. 🤷 WONTFIX.
Hier, wie so ein Dreieck aussieht:
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 1 10 45 120 210 252 210 120 45 10 1 1 11 55 165 330 462 462 330 165 55 11 1 1 12 66 220 495 792 924 792 495 220 66 12 1 1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1 1 14 91 364 1001 2002 3003 3432 3003 2002 1001 364 91 14 1 1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1 1 16 120 560 1820 4368 8008 11440 12870 11440 8008 4368 1820 560 120 16 1
Der Code
Und hier mein Code:
#!/usr/bin/env python3 # Code by DJ3EI dj3ei@famsik.de who places this work into the public domain. # You can use it however you want, or, if you prefer, under the rules of the # CC0 1.0 license https://creativecommons.org/publicdomain/zero/1.0/ . # This was inspired by https://chaos.social/@piko/114490917474879618 from io import TextIOBase from itertools import chain from sys import stdout from typing import cast def generate_pascal_triangle(wanted_lines: int) -> list[list[int]]: if wanted_lines < 1: raise ValueError( "Don't know how to construct a Pascal triangle " f"with {wanted_lines} lines." ) result = [[1]] one_zero = [0] for line_no in range(1, wanted_lines): previous_line = result[-1] result.append( list( map( lambda x, y: x + y, chain(one_zero, previous_line), chain(previous_line, one_zero), ) ) ) return result def format_pascal_triangle(triangle: list[list[int]], out: TextIOBase) -> None: str_triangle = list(map(lambda row: list(map(lambda i: str(i), row)), triangle)) if len(str_triangle) <= 1: for line in str_triangle: out.write(line[0]) out.write("\n") else: # Good position for the midpoints of the numbers of the last line, # measured in units of 1/4 of character width. last_line_midpoints = [] current_position_in_line = -4 # Compensate for the unneeded initial blank. for s in str_triangle[-1]: current_position_in_line += 4 # one blank last_line_midpoints.append(current_position_in_line + 2 * len(s)) current_position_in_line += 4 * len(s) # Same for not the last, but the previous-to-the-last line. # Try to put midpoints exactly between those of the last line. next_to_last_line_midpoints = [] for i in range(len(str_triangle[-2])): left_below = last_line_midpoints[i] right_below = last_line_midpoints[i + 1] next_to_last_line_midpoints.append((left_below + right_below) // 2) # Use such data to position any line above the midpoints. # We position either according to the midpoints of the last line # or according to the midpoints of the previous to last line, # according to our line being odd or even. def position_above_midpoints( line_to_print: list[str], midpoint_positions: list[int], skip_initial_midpoints: int, ) -> None: current_position_in_line = 0 for str_index in range(len(line_to_print)): str_to_print = line_to_print[str_index] wanted_midpoint = midpoint_positions[skip_initial_midpoints + str_index] wanted_startpoint = wanted_midpoint - 2 * len(str_to_print) initial_space_needed = wanted_startpoint - current_position_in_line out.write(" " * (initial_space_needed // 4)) current_position_in_line += (initial_space_needed // 4) * 4 if initial_space_needed % 4 >= 2: out.write(" ") current_position_in_line += 4 out.write(str_to_print) current_position_in_line += 4 * len(str_to_print) out.write("\n") for line_index in range(len(str_triangle)): line_to_print = str_triangle[line_index] if (len(str_triangle) - line_index) % 2 == 1: # line_to_print items should be above last_line items position_above_midpoints( line_to_print, last_line_midpoints, (len(str_triangle) - line_index) // 2, ) else: # line_to_print items should be above next_to_last_line items position_above_midpoints( line_to_print, next_to_last_line_midpoints, (len(str_triangle) - 1 - line_index) // 2, ) if __name__ == "__main__": from argparse import ArgumentParser parser = ArgumentParser( prog="pascal_dreieck.py", description="Print the top of Pascal's triangle to stdout", ) parser.add_argument("lines", type=int, help="The number of lines wanted.") args = parser.parse_args() triangle = generate_pascal_triangle(args.lines) format_pascal_triangle(triangle, cast(TextIOBase, stdout))
Wer dies diskutieren möchte und einen Fediverse-Zugang hat, ist eingeladen, diesen Tröt zu kommentieren.