Pascalの三角形 #2

さて、pascal1.gを元にして、DOT言語で表したグラフとして出力するコードに変更する。

pascal2.g
BEGIN {
  if (ARGC != 1) {
    print("usage: gvpr -fpascal2.g -a <step>");
    exit(0);
  }
  int step = ARGV[0];
  graph_t g = graph("pascal_triangle", "U");
  node_t p[], q[];
  int i, j;
  for (i = 0; i <= step; i++) {
# create nodes in i-th step
    q[0] = node(g, sprintf("%d_%d", i, 0));
    q[0].label = 1;
    for (j = 1; j < i; j++) {
      q[j] = node(g, sprintf("%d_%d", i, j));
      q[j].label = 0 + p[j - 1].label + p[j].label;
    }
    q[i] = node(g, sprintf("%d_%d", i, i));
    q[i].label = 1;
# create edges from (i-1)-th step to i-th step
    for (j = 0; j < i; j++) {
      edge_sg(g, p[j], q[j], "");
      edge_sg(g, p[j], q[j + 1], "");
    }
# prepare for next step
    for (j = 0; j <= i; j++) p[j] = q[j];
  }
  write(g);
}

ノードに表示される値はname属性の値が使われるが、
name属性は各ノードを区別するためのID情報であるため、
同じ表示値のノードが複数ある場合には使えない。
name属性とは無関係にノードの表示値を設定するにはlabel属性を使う。
label属性付きのノードはその表示にlabel属性の値が使われ、
この属性が無いデフォルトではname属性の値が使われることになる。
上のコードでは各ノードのname属性は二つの整数をアンダースコアで繋いだものであり、
"i_j"は{}_iC_jを意味する。
label属性には二項係数の値を設定する。

$ gvpr -fpascal2.g -a3
graph pascal_triangle {
        "0_0"    [label=1];
        "1_0"    [label=1];
        "0_0" -- "1_0";
        "1_1"    [label=1];
        "0_0" -- "1_1";
        "2_0"    [label=1];
        "1_0" -- "2_0";
        "2_1"    [label=2];
        "1_0" -- "2_1";
        "1_1" -- "2_1";
        "2_2"    [label=1];
        "1_1" -- "2_2";
        "3_0"    [label=1];
        "2_0" -- "3_0";
        "3_1"    [label=3];
        "2_0" -- "3_1";
        "2_1" -- "3_1";
        "3_2"    [label=3];
        "2_1" -- "3_2";
        "2_2" -- "3_2";
        "3_3"    [label=1];
        "2_2" -- "3_3";
}

上はPascalの三角形を0段目から3段目まで表したDOTである。
次に8段目までのグラフを描かせてみる。

$ gvpr -fpascal2.g -a8 | dot -Tpng -opascal.png