杨辉三角

关键字: 代码分享 基础算法

杨辉三角形,又称贾宪三角形、帕斯卡三角形,是二项式系数在三角形中的一种几何排列。其中第n行第i列的值为:Cin= n!/(i! * (n - i)!)。如下图所示。

为了输出一个规模为n的杨辉三角,如果根据定义来做,那每一个元素至少要计算两个阶乘,效率是很低的。从下图可知,第i行可以通过第i-1行的元素两两相加得到,这样乘法就简化成一组加法,效率会提高很多!

如果你了解函数式编程,可能知道map和reduce两个运算符:map可以保持数据维度不变,reduce则把一个n维的数据降到1维。杨辉三角属于第三类范畴:把维度为1的数据扩展成n维。

本文提供多种不同的编程语言的解决方法,来比较各语言解决此类问题的优劣。

Clojure版

(defn pascal []
  (iterate #(map + `(0 ~@%) `(~@% 0)) [1]))

(take 5 (pascal)) ; 取前面5行
; ->((1) (1 1) (1 2 1) (1 3 3 1) (1 4 6 4 1))

Clojure版是这么多实现中最简洁、最优雅的!

  1. 通过内置的倒引号操作符,非常简便地在原数组前后两端追加额外的数据0,达到扩展维度的目的;
  2. 利用现成的迭代工具iterate,无需关心迭代过程,只需专注于每次迭代的逻辑;
  3. 惰性列表让程序无需关心何时结束。

Common Lisp 版

(do ((a '(1) (mapcar #'+ `(0 ,@a) `(,@a 0))))
    ((= (length a) 10))
  (format t "~{~A~^ ~}~%" a))

Common Lisp拥有和Clojure相同的倒引号功能,但没有类似iterate的自动迭代工具,因此不得不使用do手工控制迭代过程以及结束条件。但Common Lisp优雅的format输出功能,是其他语言可欲而不可求的!

Python版

from operator import add
def pascal(n):
  a = [1]
  for i in range(n):
    yield a
    a = map(add, [0] + a, a + [0])

[a for a in pascal(10)]

适合Python 2.*版本,Python的特色是Yield生成器与列表推导。

C语言版

#include <stdlib.h>
#include <stdio.h>

#define SIZE 11

int main(void) {
  int line[SIZE] = {1};
  int row;
  int i;

  for (row = 0; row < SIZE; row++) {
    for (i = row; i > 0; i--) {
      line[i] += line[i - 1];
    }
    for (i = 0; i <= row; i++) {
      printf("%d%c", line[i], i == row? '\n': ' ');
    }
  }

  return EXIT_SUCCESS;
}
  1. C语言不能像Lisp那样便捷地生成新数组,因此一开始就开辟好足够的空间;
  2. 该版本忠实地反映出当前行如何由前一行的数据生成;
  3. C语言同样没有直接输出数组的能力,因此不得不采用循环来打印每一行;
  4. C的繁琐换来的是高性能。

批处理版

::Show a Pascal/Yanhui Triangle
@echo off
setlocal enabledelayedexpansion
set /a line[0]=1
for /l %%i in (0,1,10) do (
  for /l %%j in (%%i,-1,1) do (
    if defined line[%%j] (
      set n=%%j
      call :calc
    ) else (
      set /a line[%%j]=1
    )
  )
  for /l %%j in (0,1,%%i) do set /p=!line[%%j]! <nul
  echo.
)
goto end

:calc
set /a prev=%n%-1
set /a line[%n%]+=!line[%prev%]!
goto :eof

:end
pause

还是那句话:不要小看批处理!虽然它……很蛋疼。

Bash 版

#!/usr/bin/bash

a[0]=1
for ((i=0;i<=10;i++)); do
  for ((j=$i;j>0;j--)); do
    ((a[$j]+=a[$j-1]))
  done
  for ((j=0;j<=$i;j++)); do
    printf "%d " ${a[$j]}
  done
  echo
done

批处理都来了,怎么能少的了Shell Script。

redraiment点评各种实现,加入Python
redraiment[Thanks Ruby]杨辉三角