Perl 如何修改程序以打印Pascal';s三角形?

Perl 如何修改程序以打印Pascal';s三角形?,perl,Perl,首先,帕斯卡的三角形是这样的: 您看到的第一行是第0行 这没什么不寻常的 当你是计算机科学家的时候 帕斯卡三角形中的每个项都可以通过以下公式进行预测: C(n,k)=n!/[k!*(n-k)!],其中“n”是行,“k”是从零到n的任意整数 因此,帕斯卡三角形可以用(n,k)组合来预测: 这就是你在上图中看到的 帕斯卡三角形基本上是二项式概率: (H+T)^n#将双面硬币掷“n”次,它落在“正面”或“反面”,然后收集一组系数中每个系数的频率,对于n=3,我们得到展开式: (H+T)^3=1(H

首先,帕斯卡的三角形是这样的:

您看到的第一行是第0行

这没什么不寻常的 当你是计算机科学家的时候

帕斯卡三角形中的每个项都可以通过以下公式进行预测:

C(n,k)=n!/[k!*(n-k)!],其中“n”是行,“k”是从零到n的任意整数

因此,帕斯卡三角形可以用(n,k)组合来预测:

这就是你在上图中看到的

帕斯卡三角形基本上是二项式概率:

(H+T)^n#将双面硬币掷“n”次,它落在“正面”或“反面”,然后收集一组系数中每个系数的频率,对于n=3,我们得到展开式:

(H+T)^3=1(H^3)+3(H^2)(T)+3(H)(T^2)+1(T^3),其中这些系数: 1,3,3,1在帕斯卡三角形的第3行


我定义了一个阶乘(!)和一个组合,并能够得到 Pascal三角形任意一行上的系数数,带有一些循环Perl代码:

use strict;
use warnings;

# Note the first row is row 0.
print("\nWhich row of Pascal's triangle to display: ");
my $row = <STDIN>; # The row that you want to display # This is also n. 
my $terms = $row + 1; # The number of terms is one more than the row number. 

Pascal_Row($row); # Print the Pascal numbers for that row. 

# Function displays the numbers for a row of Pascal's triangle. 
#######################################################
sub Pascal_Row
{
    my $row = shift; # Row is passed in. 

    for(my $k = 0; $k < $row + 1; $k++) # k alternates, but not the row which is n. 
    {
        print(combination($row, $k), "\t") # Print each row. 
    }
    print("\n"); # Print a newline after each time this function is called.
}

# This computes the factorial of a number.
###########################################
sub factorial
{
    my $number = shift; # argument. 
    my $factorial_number = 1; # initalize the factorial. 

    for(my $i = 1; $i <= $number; $i++)
    {
        $factorial_number *= $i; # compute the factorial, by multiplying all terms up to and including number.
    }

    return $factorial_number; # Return the factorial number. 
}

# Computes a matehmatical combination usually denoted as C(n, k)
# where n is the row number, and k is each item in a row of Pascal's traingle 
sub combination
{
    my($n, $k) = @_; # from input. 

    # This is the mathematical formula for a combination. 
    my $combination_number = factorial($n) / (factorial($k) * factorial($n - $k));

    return $combination_number # And returning it. 
}
对于Pascal三角形的第8行,这是完全正确的。如果我将它从Pascal三角形的第0行循环到第8行,我将得到所有正确的Pascal三角形行,但它看起来不像三角形(它看起来更像一个方框),因此我如何修改代码来调整缩进

如果要显示8行Pascal三角形,如何决定第一行缩进多少?我怎样才能做一个“三角形”

左对齐三角形:

my $MAX_VAL_SIZE = 5;

for my $n (0...$N) {
   my @row;
   for my $k (0..$n) {
      push @row, C($n, $k);
   }

   say join "  ", map sprintf("%*d", $MAX_VAL_SIZE, $_), @row;
}
sub center {
   my ($n, $s) = @_;
   my $pad_len = $n - length($s);
   my $pad_len_l = int($pad_len/2);
   my $pad_len_r = $pad_len - $pad_len_l;
   return ( " " x $pad_len_l ) . $s . ( " " x $pad_len_r );
}

my $MAX_VAL_SIZE = 5;

for my $n (0...$N) {
   my @row;
   for my $k (0..$n) {
      push @row, C($n, $k);
   }

   my $row = join "  ", map center($MAX_VAL_SIZE, $_), @row;
   say center(($N+1)*($MAX_VAL_SIZE+2)-2, $row);
}
中心三角形:

my $MAX_VAL_SIZE = 5;

for my $n (0...$N) {
   my @row;
   for my $k (0..$n) {
      push @row, C($n, $k);
   }

   say join "  ", map sprintf("%*d", $MAX_VAL_SIZE, $_), @row;
}
sub center {
   my ($n, $s) = @_;
   my $pad_len = $n - length($s);
   my $pad_len_l = int($pad_len/2);
   my $pad_len_r = $pad_len - $pad_len_l;
   return ( " " x $pad_len_l ) . $s . ( " " x $pad_len_r );
}

my $MAX_VAL_SIZE = 5;

for my $n (0...$N) {
   my @row;
   for my $k (0..$n) {
      push @row, C($n, $k);
   }

   my $row = join "  ", map center($MAX_VAL_SIZE, $_), @row;
   say center(($N+1)*($MAX_VAL_SIZE+2)-2, $row);
}

这是一个棘手的问题,因为数字的不同宽度对布局很重要

每行需要缩进行中数字间隔的一半,适当乘以(最后一行为零,第一行为1)——也就是说,如果数字本身宽度相等

但事实并非如此,前几行除外;这些数字占用不同的空间。一种补救方法是对数字使用固定宽度,并使用该宽度调整缩进和分隔

首先计算所有行,以便找到数字的最大宽度

use warnings;
use strict;
use feature 'say';
use List::Util qw(max);

my $max_row = (shift || 8); 

my @rows = map { pascal_row($_) } 0..$max_row-1;

my $max_num_wd = max map { length } @{$rows[-1]};
my $pad = 1;  # choice (must be non-zero)
my $sep     = ' ' x ($max_num_wd + 2*$pad);
my $lead_sp = ' ' x ($max_num_wd +   $pad);

for my $n (0..$#rows) {
    say $lead_sp x ($max_row-1-$n),
        join $sep, map { sprintf "%${max_num_wd}d", $_ } @{$rows[$n]};
}

sub pascal_row {
    my ($row) = @_;
    return [ map { n_over_k($row, $_) } 0..$row ];
}

sub n_over_k {
    my ($n, $k) = @_; 
    return factorial($n) / (factorial($k) * factorial($n - $k));
}

sub factorial {
    my ($n) = @_;        
    my $fact = 1;
    $fact *= $_ for 2..$n;
    return $fact;
}
这将打印正确的布局。
$pad
是一个任意整数,用于最大数字宽度上的额外空间,用于缩进和分隔;必须大于0才能协调它们。(分离需要上一行中居中数字的左右两侧的空间,因此系数为2。)


原始代码,在计算时打印,因此手动提前设置
$max\u num\u wd


这将打印一个正确的布局,数字宽度不超过4位,或者需要调整
$max\u num\u wd

以下是另一种方法:

use strict;
use warnings;

sub fact {
  my $n = shift;
  return 1 if $n < 1;
  return $n * fact($n - 1);
}

sub n_over_k {
  my $n = shift;
  my $k = shift;
  return fact($n) / ( fact($k) * fact($n - $k) );
}

sub pascal_row {
  my $n = shift;
  return map { n_over_k($n - 1, $_) } (0 .. $n - 1);
}

my $n = shift || 8;

# $maxw is the first odd width where the biggest number will fit
my $max = 0;
map { $max = $_ if $_ > $max } pascal_row($n);
my $maxw = length('' . $max);
$maxw += ($maxw + 1) % 2;

# Print the Pascal´s triangle
foreach my $i (1..$n) {
  print ' ' x ( ( $maxw + 1 ) * ($n - $i) / 2 );
  foreach my $j ( pascal_row($i) ) {
    printf "%${maxw}d ", $j;
  }
  print "\n";
}
13的帕斯卡三角形:

# pascal.pl 13
                          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

您可以不使用任何组合公式生成三角形

这样做的原因是,这是最有效的方法

其基本思想是采用观察法,即下一步的价值观 行是位于上面的两个元素的总和

这个解决方案也是一个很好的例子,说明了如何使用 (对)数组的引用

一个有趣的特性是缩进是从 最后一行中的元素(具有最大值)

要提供三角形的漂亮外观,单元格大小必须是偶数。 “基本”缩进是该尺寸的一半。 每行的实际缩进量是该基本尺寸乘以相应的 编号,从行索引和总行数派生

整个脚本如下所示:

use strict;
use warnings;
use feature qw(say);
use POSIX qw(ceil);

my $rowCnt = 14;        # How many rows
say "Pascal Triangle with $rowCnt rows:";
# Rows container, filled with a single row (containing single 1)
my @rows = ([ 1 ]);
my ($lastRow, $row, $ind);
# Generate / add further rows
for ($ind = 1; $ind < $rowCnt; $ind++) {
  $lastRow = $rows[$#rows]; # Last row gathered so far
  push(@rows, getNextRow($lastRow));
}
$lastRow = $rows[$#rows];
# Middle elem. of the last row
my $midElem = $$lastRow[($rowCnt - 1) / 2];
# No of digits + separator, rounded up to even
my $elemSize = ceil((length($midElem) + 1) / 2) * 2;
my $shf = $elemSize / 2;    # Shift size for a sigle step
# Print rows
for ($ind = 0; $ind < $rowCnt; $ind++) {
  my $row = $rows[$ind];
  my $spc = $shf * ($rowCnt - $ind - 1);
  printRow($spc, $row, $elemSize);
}

sub getNextRow {    # Create the next row and return the reference to it
  my $lastRow = $_[0];  # Read param
  my @row = (1);    # Start the new row from a single 1
  for (my $i = 0; $i < $#$lastRow; $i++) {
    push(@row, $$lastRow[$i] + $$lastRow[$i + 1]);
  }
  push(@row, 1);    # Add terminating 1
  return \@row;     # Result - reference to the created row
}

sub printRow {      # Print a row of the triangle
  my ($leadSpc, $row, $elemSize) = @_;  # Read params
  # Leading spaces and the initial element (always 1)
  printf("%s1", ' ' x $leadSpc);
  # Print the rest of the row
  for (my $i = 1; $i <= $#$row; $i++) {
    printf("%*d", $elemSize, $$row[$i]);
  }
  print("\n");
}
使用严格;
使用警告;
使用特征qw(例如);
使用POSIXQW(ceil);
我的$rowCnt=14;#几排
说“带$rowCnt行的Pascal三角形:”;
#行容器,填充一行(包含单个1)
我的@rows=([1]);
我的($lastRow,$row,$ind);
#生成/添加更多行
对于($ind=1;$ind<$rowCnt;$ind++){
$lastRow=$rows[$#rows];#到目前为止收集的最后一行
推送(@rows,getNextRow($lastRow));
}
$lastRow=$rows[$#rows];
#中间元素。最后一排的
my$midElem=$$lastRow[($rowCnt-1)/2];
#位数+分隔符,四舍五入为偶数
my$elemSize=ceil((长度($midElem)+1)/2)*2;
my$shf=$elemSize/2;#单步移动的大小
#打印行
对于($ind=0;$ind<$rowCnt;$ind++){
my$row=$rows[$ind];
my$spc=$shf*($rowCnt-$ind-1);
printRow($spc,$row,$elemSize);
}
sub getNextRow{#创建下一行并返回对它的引用
my$lastRow=$[0];#读取参数
my@row=(1);#从单个1开始新行
对于(我的$i=0;$i<$#$lastRow;$i++){
推送(@row,$$lastRow[$i]+$$lastRow[$i+1]);
}
按(@行,1)#添加终止1
返回\@row;#Result-对已创建行的引用
}
子打印行{#打印三角形的一行
我的($leadSpc,$row,$elemSize)=@35;读取参数
#前导空格和初始元素(始终为1)
printf(“%s1”,“x$leadSpc”);
#打印行的其余部分

对于(我的$i=1;$i你知道你将有多少行,这意味着你知道从底部开始有多少行。每行上的第一个数字位于下面一行上的第一个和第二个数字的中间,因此它必须被推过一行上的数字所用间距的一半。有了这个…乘法!在这种情况下,我应该吗不是用制表符将行上的每个系数分开吗?是的,一路上您会遇到制表符停止的问题。我建议查看固定宽度输出的
printf
:)您能解释一下吗?我的头脑完全崩溃了!
center(N,STRING)
将字符串填充到N个字符,将填充拆分到字符串两侧以使其居中。因此
居中(5,1)
返回
␠␠1.␠␠。这是为每个数字以及整个行完成的。感谢您的解释。您的修订
use strict;
use warnings;
use feature qw(say);
use POSIX qw(ceil);

my $rowCnt = 14;        # How many rows
say "Pascal Triangle with $rowCnt rows:";
# Rows container, filled with a single row (containing single 1)
my @rows = ([ 1 ]);
my ($lastRow, $row, $ind);
# Generate / add further rows
for ($ind = 1; $ind < $rowCnt; $ind++) {
  $lastRow = $rows[$#rows]; # Last row gathered so far
  push(@rows, getNextRow($lastRow));
}
$lastRow = $rows[$#rows];
# Middle elem. of the last row
my $midElem = $$lastRow[($rowCnt - 1) / 2];
# No of digits + separator, rounded up to even
my $elemSize = ceil((length($midElem) + 1) / 2) * 2;
my $shf = $elemSize / 2;    # Shift size for a sigle step
# Print rows
for ($ind = 0; $ind < $rowCnt; $ind++) {
  my $row = $rows[$ind];
  my $spc = $shf * ($rowCnt - $ind - 1);
  printRow($spc, $row, $elemSize);
}

sub getNextRow {    # Create the next row and return the reference to it
  my $lastRow = $_[0];  # Read param
  my @row = (1);    # Start the new row from a single 1
  for (my $i = 0; $i < $#$lastRow; $i++) {
    push(@row, $$lastRow[$i] + $$lastRow[$i + 1]);
  }
  push(@row, 1);    # Add terminating 1
  return \@row;     # Result - reference to the created row
}

sub printRow {      # Print a row of the triangle
  my ($leadSpc, $row, $elemSize) = @_;  # Read params
  # Leading spaces and the initial element (always 1)
  printf("%s1", ' ' x $leadSpc);
  # Print the rest of the row
  for (my $i = 1; $i <= $#$row; $i++) {
    printf("%*d", $elemSize, $$row[$i]);
  }
  print("\n");
}