Coach has asked for the wisdom of the Perl Monks concerning the following question:

Hello everyone! Forgive me for this beginner's question, but I am working on a maths paper and for that paper I am required to generate Pascal's triangle. I found the Perl code for doing this here http://www.tug.org/TUGboat/Articles/tb28-3/tb90mertz.pdf , p.357. The problem is that this code generates a triangle that is squashed "against the wall" and is not equilateral. I would be extremely grateful if someone could tell me how to alter the code so as to make the triangle equilateral, or if this is even possible? I don't have much experience in Perl, but I am willing to learn and I desperately need the triangle for my paper. Thank you so much!
  • Comment on Equilateral Pascal's Triangle in perltex

Replies are listed 'Best First'.
Re: Equilateral Pascal's Triangle in perltex
by Perlbotics (Archbishop) on Jun 01, 2009 at 17:42 UTC

    The main trick is to double the number of columns plus one and to fill the void symmetrically...

    use strict; sub pascalMatrix { my $tabularRows = ""; my @row = (1); my $cols = ($_[0] * 2) + 1; #add: final number of columns my $filler = "{ }"; #add: used to mark an empty entry for (my $r = 0; $r <= $_[0]; $r++) { # Output the current row ### begin of modification # insert a filler between each item (sprintf is for LaTeX cosmeti +cs) my @fmt = map { (sprintf("%3d",$_), $filler) } @row; pop @fmt; # pre-/append a filler until final number of columns reached unshift(@fmt,$filler), push(@fmt,$filler) while (@fmt < $cols); $tabularRows .= join (" & ", @fmt) ." \\\\\n"; ### end of modification # Generate the next row my @nextRow = (1); for (my $c = 1; $c <= $r; $c++) { push @nextRow, @row[$c - 1] + @row[$c]; } push @nextRow, 1; @row = @nextRow; } return "\\begin{tabular}{*{$cols}{c}c}\n" . # changed! $tabularRows . "\\end{tabular}\n"; } print pascalMatrix(10);
    This was the output of the original version:
    \begin{tabular}{*{5}{c}c} 1 \\ 1&1 \\ 1&2&1 \\ 1&3&3&1 \\ 1&4&6&4&1 \\ 1&5&10&10&5&1 \\ \end{tabular}
    This is the output of the patched version:
    \begin{tabular}{*{11}{c}c} { } & { } & { } & { } & { } & 1 & { } & { } & { } & { } & { } \\ { } & { } & { } & { } & 1 & { } & 1 & { } & { } & { } & { } \\ { } & { } & { } & 1 & { } & 2 & { } & 1 & { } & { } & { } \\ { } & { } & 1 & { } & 3 & { } & 3 & { } & 1 & { } & { } \\ { } & 1 & { } & 4 & { } & 6 & { } & 4 & { } & 1 & { } \\ 1 & { } & 5 & { } & 10 & { } & 10 & { } & 5 & { } & 1 \\ \end{tabular}
    My LaTeX installation is gone, but there's a nice LaTeX Online-Compiler available.
    HTH

    Update: The code presented above is a runnable Perl program. In order to make it work with LaTeX you need to get rid of use strict; and print pascalMatrix(10);. Furthermore, replace sub pascalMatrix by \perlnewcommand{\pascalMatrix}[1]. Remove, rename, or uncomment the previous version of \pascalMatrix.

      do not forget \usepackage{perltex} so the whole looks like:

      \documentclass{article} \usepackage{perltex} \perlnewcommand{\pascalMatrix}[1] { my $tabularRows = ""; my @row = (1); my $cols = ($_[0] * 2) + 1; #add: final number of columns my $filler = "{ }"; #add: used to mark an empty entry for (my $r = 0; $r <= $_[0]; $r++) { # Output the current row ### begin of modification # insert a filler between each item (sprintf is for LaTeX cosmeti +cs) my @fmt = map { (sprintf("%3d",$_), $filler) } @row; pop @fmt; # pre-/append a filler until final number of columns reached unshift(@fmt,$filler), push(@fmt,$filler) while (@fmt < $cols); $tabularRows .= join (" & ", @fmt) ." \\\\\n"; ### end of modification # Generate the next row my @nextRow = (1); for (my $c = 1; $c <= $r; $c++) { push @nextRow, @row[$c - 1] + @row[$c]; } push @nextRow, 1; @row = @nextRow; } return "\\begin{tabular}{*{$cols}{c}c}\n" . # changed! $tabularRows . "\\end{tabular}\n"; } \begin{document} \pascalMatrix{10} \end{document}
      Thank you so much for your reply! I tried running the code you posted with perltex, but it gives the following errors (1)Undefined control sequence (2)end occured inside a group at level 3. This is probably some stupid mistake, but I just can't figure it out.
Re: Equilateral Pascal's Triangle in perltex
by Corion (Patriarch) on Jun 01, 2009 at 16:05 UTC

    If you know how wide your triangle will be at the bottom, it shouldn't be too hard for you to indent the numbers at the top so that half of the numbers are left of the middle and half of them are to the right. As you are certainly aware (and a short induction shows), the count of numbers increases by one for each row you add to the bottom. The width of each number can be determined either experimentally or estimated. You will then need to output ($count_of_numbers * $number_width - $number_width_of_first_number_in_the_triangle) /s spaces to align the first row to the center, then output the first row. Then repeat the calculation for the second row of the triangle and output as many spaces as necessary before printing the numbers in the second row.

Re: Equilateral Pascal's Triangle in perltex
by zentara (Cardinal) on Jun 01, 2009 at 16:19 UTC
    I didn't check out the pdf, but using a Tk::Canvas, Tk::Zinc, Gnome2::Canvas, or the Goo Canvas would make it simple. A simple trick to remember, is that an equilateral triangle, must be equi-angular.

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
Re: Equilateral Pascal's Triangle in perltex
by Khen1950fx (Canon) on Jun 02, 2009 at 00:07 UTC
    This might help you:

    Pascal's Triangle. It has a couple of interactive versions that'll help you visualize what's going on.

    Update: I've been looking for a couple of hours, just to satisfy my curiosity, and I finally found it. See Tie::Math under the "How about Pascal's Triangle" section:

    #!/usr/bin/perl use Tie::Math qw(f X Y); my %pascal; tie %pascal, 'Tie::Math', sub { if( X <= Y and Y > 0 and X > 0 ) { f(X,Y) = f(X-1,Y-1) + f(X,Y-1); } else { f(X,Y) = 0; } }, sub { f(1,1) = 1; f(1,2) = 1; f(2,2) = 1; }; #'# my $height = 5; for my $y (1..$height) { print " " x ($height - $y); for my $x (1..$y) { print $pascal{$x,$y}; } print "\n"; }

    Update: fixed, added comma after "Tie::Math" line 6 and right curly on line 12.

      Thank you everyone for your helpful advice! However, I am still struggling. I should have perhaps made my level of abilities more clear: when it comes to perl, I know absolutely nothing! The only thing I know is, that one is supposed stick the code into a text editor, stick the LaTeX commands around it, and then run it with perltex. Evidently, with code posted by Perlbotics, I am supposed to do something far more advanced?
        Back to my suggestion of using an advanced Canvas type widget.........

        On most canvases you can rotate items, so imagine this construction technique for equilateral triangles.

        Create 3 identical line segments, stacked right on top of each other. Leave the bottom segment as is. The next segment is then rotated around its left end point, x degrees, in a counterclockwise direction. The last segment is rotated clockwise around its right endpoint, x degrees.

        The whole segment set can be a "group", and the resulting triangle can be translated (moved) as a group, and rotated to any angle.

        Calculating the x degree value depends on the original line segment lengths chosen, and can be calculated with some simple trig. Of course, for an equilateral triangle, it's just 60 degrees.

        BTW, Coach, are you trying to find a way to make "play diagrams" for a team? :-) If so, you might want to check out Tk Patio/Office layout designer


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku
Re: Equilateral Pascal's Triangle in perltex
by Limbic~Region (Chancellor) on Jun 02, 2009 at 14:10 UTC
    Coach,
    I know what you are looking for is a quick solution to your problem so you can move on. There are plenty of images of Pascal's Triangle that can be found on google or web apps that generate them. Since you are student and your job is to learn, I will tell you how I would do it programmatically if it were me.

    The first thing I would do is ask how many rows the user wanted to generate. This will tell me two pieces of valuable information. The first is what the largest number in the triangle will be. If you are not sure why this is, I will remind you of the relationship between Pascal's Triangle and combinatorials (C = N choose K). The second piece of information is how many leading units you need for the first row. What do you mean by unit you ask - read on.

    The next thing I would do is determine the length of the largest number in the triangle. This will become 1 unit. Every value (space or number) will be this wide. This will make the triangle look as smooth as possible (assuming monospace font).

    Finally, I would start filling in each row. Filling up the leading white space units and then placing each number. Each number will need to be centered in the unit - probably using sprintf. If the triangle still does not look smooth it is probably a result of imperfect centering. You may be able to remedy this by varying which side to leave the extra space on (round robin, random, odd/even, etc).

    You probably already have your solution and have moved on but if you would like to really spend some time learning perl - please feel free to ask questions and we will help you out as best as we can.

    Cheers - L~R