RustにいればRustに従え(8) シェルピンスキーのギャスケット、裏Pascal三角形

Joseph Halfmoon

前回Pascalの三角形を描いた後、@rithmety様のご指導あり。iteratorの書き方をお教えいただいた上、段数バグっていた(1段少なかった)件も判明。ありがとうございます。今回はPascalの3角形に基づくシェルピンスキーのギャスケット(近似)の描画です。実はPascalの3角形、意外と難物?だった。

※『RustにいればRustに従え』関係記事 index はこちら

※動作確認は、Windows11のWSL2上にインストールしたUbuntu20.04LTS上のrustc 1.64.0 (a55dd71d5 2022-09-19) で行っています。

画像をprint? pbm形式

前々回、マンデルブロ集合を「描いた」ときは、テキストファイルでカラー画像を表現できるppmフォーマットを使用させていただきました。今回のシェルピンスキーのギャスケットは、白黒2値で表現できるので、より簡単なpbm形式を使用いたします。これまたUnix世界では古典的なテキストで画像を表現できるフォーマットであります。昔の形式など知らんという画像ビューワーしかない場合、定番の画像処理ツール Imagemagick のconvertコマンドなどがインストールされていれば変換できると思います。

シェルピンスキーのギャスケットとPASCAL三角形

シェルピンスキーのギャスケットは「ありがちな」フラクタル図形なのでご覧になったことがあるのでないかと。なにやら三角形の繰り返しで、フラクタルというには簡単?な印象もうけます。Pascalの三角形との関係でいえば、Pascalの3角形の各段、各項の偶奇を判断して、偶数ならば白、奇数ならば黒という2値変換をすれば描画していくことができます。

しかし、フラクタルはフラクタルです、深淵が隠れておりますで。真のシェルピンスキーのギャスケットを描くには無限段のPascal三角形が必要と。今回のように有限の段数で打ち切って描いたものは、あくまで

近似

でしかありません。

その上、Pascalの3角形から「変換」する方式でそこそこの大きさのシェルピンスキーのギャスケットを描けると思っていたら、大間違いっす。ちょっと考えたら分かることだったのですが、Pascalの3角形の中央付近の項は「倍々ゲーム」で数値が増大していきます。つまり、

i32型を使った前回の方法で32段は描けるけどその後溢れる

のでした。そこで今回のソースではi64型に変更してみましたが、そんなもの焼石に水です。Pascal恐るべし。もっと大きなシェルピンスキーのギャスケット(近似だけれども)を描く場合はPascalの3角形から計算するという方式は諦めた方がよさそうです。

今回実験したRustのソースコード

今回のコードも mut は無ですが、ピクセルを「print」するところでforを3つも使ってます。まあ、forはシンタックス・シュガーみたいだし、printするときはforの方が分かりやすいしいいか。

前回との違いは、前回が数値(文字)で出力したのに対して、今回はピクセル(pbmでは実際は0か1の文字だけれども)であることくらいです。かなり「洗練」されたコードになっているのは@rithmety様のお陰であります。

fn print1line(halfwidth: usize, arg: Vec<i64>) {
    let fillw : usize = halfwidth - arg.len();
    for _x in 0..=fillw {
        print!("{} ", 0);
    } 
    for xa in arg.iter() {
        print!("{} ", if xa % 2 == 0 {0} else {1});
        print!("{} ", 0);
    } 
    for _x in 0..fillw {
        print!("{} ", 0);
    }
    println!(); 
}

fn sub(arg: Vec<i64>) -> Vec<i64> {
    [1]
        .iter().cloned()
        .chain(arg.windows(2).map(|s| s[0] + s[1]))
        .chain([1].iter().cloned())
        .collect()
}

fn sub2(w: usize, arg: Vec<i64>) -> Vec<i64> {
    print1line(w, arg.clone());
    sub(arg)
}

fn main() {
    let pic_size = 64usize;
    println!("P1");
    println!("{} {}", pic_size * 2 + 1, pic_size);
    print1line(pic_size, vec![1]);
    let test = vec![1i64, 1];
    (2..=pic_size).fold(test, |v, _| sub2(pic_size, v));
}
動作結果

上記のコードを動作させた結果のテキスト出力をsierpinski.pbmにリダイレクトし、画像ビューワーNkVで確認したものが以下に。NkVはWindows上の画像ビューワーですが軽くて、いろいろできるけれどもインタフェースが控えめ、勿論pbm形式にも対応なのでWSL2上のUbuntuからも愛用させていただいております。Sierpinski64

それらしい図形(近似)は描けたけれども、この方法では先はないのう。

RustにいればRustに従え(7) Pascalの三角形を計算、mut無、for 1 へ戻る

RustにいればRustに従え(9) 方程式の求解、Newton法テキトー版mut無for無 へ進む