🤪

円周率100億桁の出力

2022/11/29に公開

円周率100億桁を自作のPythonプログラムで求めた。となると、次の欲求は。

円周率100億桁をこの目でみてみたい。

というわけで、いろいろやってみた記録である。

※ 2022. 11月末現在……試行錯誤中。

円周率100億桁をどうやって見るか

円周率100億桁のあたまと終わりは、チャレンジのページにも掲載したが、円周率を100億桁求めたら、ひとは頭の何桁かと、あとわかっている場合 (世界記録とかではなく、リファレンスもある場合)、末尾の何桁かを確認して、「ふぅ♡」と満足するのが普通だろう。おそらく、大抵のひとにとって『7億6654万2310桁目』とか『68億0327万9912桁目』などには用はない。

んじゃ、円周率の中間の桁をすっとばして最後を求める手法が知られているんだから、最初と最後だけ求めればいいじゃん? と思いませんか? (※ いえ思いません)

ということで円周率100億桁全桁をなにかに出力してみてみたい。別にプリントアウトでなくてもなんでもいい。動画にしてパラパラみてもよいし、3Dのオブジェにして鑑賞してみてもよい。最悪『さんてんいちよん……』と読めなくても、そこに意味のある (※ ねーよ) 情報がある、というだけでもよいのだ。

まずはプリントアウト?

まあ基本は紙に出力でしょ。1mm四方くらいの文字にしたらいけんじゃね? と思ったが……。

100億桁というのは、すごい (※ 小学生かよ)。なにがすごいって、10かける10 (100) かけることの、1万かける1万 (億) である。数字を1mm四方にしたとしても、1mmってのは1mに1000個しか並ばないのを、ご存知だろうか? つまりは100mないと10万並ばない。100m四方つまり東京ドーム何個分だっけ、とにかく1ヘクタールにびっしり1mm四方の文字を敷き詰めて、やっと100億なんだぜ。

余談だが、円周率100億桁を求めるときの自分内合言葉は、「地球の総人口を上回ってやれ」であった。すなわち、地球人を縮小して1mm四方に並べたとして、100mとはいわんがまあ90mとか四方の敷地が必要なわけだ。いや人口多すぎ。地球が温暖化するわけだわ。

もうひとつ余談だが、円周率100億桁といってもテキストファイルで10GB程度である。チップが5mm角くらいの (知らんけど) フラッシュメモリでもその100倍もの1TBなんていう製品が出ている。つまりバイトあたり一粒にしても100万 × 100万の粒粒を、5mm四方に並べているっていうことである。も〜、どんだけの密度で詰めてるんだ?

だから円周率100億桁をコンパクトに一覧 (?) できるように並べるならフラッシュメモリくらい (か、その2桁くらい下) のオーダが妥当なんだろうが、そもそも5nmとか500nm (ナノメートル = 100万分の1mm) の粒粒が見えるわけない。コロナウイルスが100nmなので、見えてしまったら結構、日常生活がストレスだらけだろう。その上、どんなに大きな粒粒でも溜まった電荷なんて見えるわけがない。つまり、二重に見えない(笑)。

以上より1mmではなまぬるいことがわかった (1mmだって見えねーって)。ここで、職場の高性能高速レーザプリンタの解像度を調べてみる。カタログスペックでは『横2400 × 縦1200dpi』である。つまり、横は1インチ == 25.4mmに2400ドット、すなわち0.01mm (10ミクロンは昭和で廃止 \mum) くらいのドットで文字を構成すれば、限りなくたくさん入るに違いない。数字だから、桁の間も入れて、1文字が横4 × 縦6ドット (後出) あればまあ読めるだろう (※ 小さすぎて読めない)。おっと縦はその半分だから、横も1200dpiに合わせたとして1ドット0.02mm、横4 × 縦6ドットだとすると0.08 × 0.12mmくらいの文字ということになる。

A4用紙が横20cm、縦27cmくらいなので、横2500 × 縦2250文字入る。つまり……1ページに450万文字も詰められる!!

……と思ったら、それでも100億文字を印字するためには、2200ページ以上必要なんですね。両面印字したとして1100枚、厚さ約10cmの紙の束ができる。しかも絶対に読めんけど。ただまあ、もともと読めたって意味のない数字だ、いちおう「100億桁出力しました」って言えるだろう。

では1200dpiで、4 × 6ドットで構成された文字を出力してみることにする。

いきなり、追伸

まずは、プリンタに何を送って絵を描かせるか、である。後で詳しく書く (結局それやってしまった) が、白黒2値の画像だってPNGとかTIFFとかの無 (可逆) 圧縮だって、プリンタに送ってしまうとアンチエイリアス→ディザされてしまう恐れがある。要は、プリンタがトナー (インク) を出す・出さないを決定する内部的なパレットの1マス1マスを『黒』『白』にプログラムする手段が必要だ。何を言ってるのかわからねーと思うが。

うまくいくかどうかわからないが、得意の(笑) Postscript言語でプログラムを書いてみた。これだって内部でアンチエイリアス・ディザされないとは限らないが、Postscriptプリンタで内部解像度 (の約数) で描けば、一番内部パレットに近いところに絵を描きそうな気がする。

Postscriptとわ

Postscriptっていうのは、プリンタにこうこうこういう絵を描け、と命令する仕掛けである。Adobeが1980年代に開発した。ところが、プリンタにデータを送るだけにしてはオーバースペックで、変数はもちろん、ループはあるわ判断はあるわ関数定義はできるわ、で、もちろんチューリング機械なわけだから、わしはこれで8-Queenとかを解いたこともある。つまり、プリンタにプログラムを送るとプリンタがパズルを解いて、紙に答を出力する。

ちなみに、後のPDFである。

まあプリンタの、スペックの低いCPUで円周率計算からやらせるのは非現実的だろうから (できることはできるが) いずれ計算結果をPostscriptプログラムに埋め込んで送ることになるわけだけど、任意字形でプリントしやすいとか、ここでの最大の目的的に『プリンタにアンチエイリアス・ディザされることなく(?)ビットマップを出したい』というのには最適と思われる。

Postscriptでお絵描き

さいしょ、Postscriptらしく『フォントを定義して文字で……』とか思ったんだけど、やり方を忘れてしまったので、てきとーに3 × 5ドットの文字 (7セグメントLEDっぽい。桁・行間入れて4 × 6にしてある) を描くルーチンにフォントデータを喰わせた文字ごとのルーチンを、1文字ずつ切り取った文字列に応じて呼び出し分けるようにした。以下のようになる。ここでは分割して掲載するが、まとめて1個にして最後にデータを描く部分をくっつけて、『なんとか.ps』という名前にすれば、とりあえずlprでプリンタに送ったりghostviewや『プレビュー』 (macOS付属) で観たりできる。

Postscriptでお絵描き (プログラムリスト)

PSヘッダ

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox:  14 14 581 827
%%DocumentMedia: A4 595 842 80 white ( )
%%Orientation: Portrait
%%EndComments
%%BeginDefaults
%%PageMedia: A4
%%PageOrientation: Portrait
%%EndDefaults
%%BeginSetup
<< /PageSize [595 842] >> setpagedevice
%%EndSetup

gs (ghostscript) でデバッグするだけなら頭の2行だけでOKだけど、使用したプリンタ (Xerox Docuprint C4000d) ではこのままではレターサイズ? の紙を要求してくるので、A4の紙にプリントさせるために、他のA4でうまく出ている例からヘッダを切り取ってきて使った。全部必要かどうかは知らん。

定数とか

/dotsize 0.12 def
%/dotsize 1.2 def
/charsperline 10000 def
/topy 780 def
/topx 58 def

Postscriptは左下原点 (数学と同じ座標系) なので、画面座標系慣れしている場合は注意。

あと、/名前 定義されるもの defの順番なので、LISP慣れしている場合は注意。

単位だが、すべてpt (ポイント) である。ポイントは長さの単位であって、決して『NHKの文系記者が開発した単位・「パーセント」のことをわざわざbuzz wordでセンセーショナルに呼ぶための単位』ではない。

Postscriptではbig pointといって1/72inchが1pointである (Knuth先生のTeXブックだったかMetafontブックだったかに詳しい)。だからドットサイズを1ptにすると72dpi、0.12ptにすると600dpiになる。

諸手続き

/toppage { topx topy moveto } def

/drawdot { currentpoint 2 copy newpath moveto
           dotsize 0 rlineto 0 dotsize rlineto
           dotsize neg 0 rlineto 0 dotsize neg rlineto
           closepath fill moveto drawspc } def
/drawspc { currentpoint newpath moveto
           dotsize 0 rmoveto } def
 
/drawchar {
  aload pop
  0 1 5 {
    pop
    0 1 3 {
      pop 1 eq { drawdot } { drawspc } ifelse
    } for
    dotsize neg 4 mul dotsize rmoveto
  } for
  dotsize 4 mul dotsize neg 6 mul rmoveto
} def

PSの文法を解説する気はないのだが、自分メモも兼ねて注意点をいくつか。

まずnewpathしたりfillしたりすると、現在座標は忘れてしまう。だからcurrentpointでスタックに(y、x)を退避しておいてmovetoしたり、それを2 copyして描き終わってから再度移動したり、というのが必要である。

あと{ ... } for の実行ブロックに突入した直後は、ループ回数がスタックに積まれているので、特に利用する必要がなければ一個popしておく。

フォントデータと数字を描く手続き

% bitmap order from right->left, above->below
/font0 [ 0 0 0 0  1 1 1 0  1 0 1 0  1 0 1 0  1 0 1 0  1 1 1 0 ] def
/font1 [ 0 0 0 0  1 0 0 0  1 0 0 0  1 0 0 0  1 0 0 0  1 0 0 0 ] def
/font2 [ 0 0 0 0  1 1 1 0  1 0 0 0  1 1 1 0  0 0 1 0  1 1 1 0 ] def
/font3 [ 0 0 0 0  1 1 1 0  1 0 0 0  1 1 1 0  1 0 0 0  1 1 1 0 ] def
/font4 [ 0 0 0 0  1 0 1 0  1 0 1 0  1 1 1 0  1 0 0 0  1 0 0 0 ] def
/font5 [ 0 0 0 0  1 1 1 0  0 0 1 0  1 1 1 0  1 0 0 0  1 1 1 0 ] def
/font6 [ 0 0 0 0  1 1 1 0  0 0 1 0  1 1 1 0  1 0 1 0  1 1 1 0 ] def
/font7 [ 0 0 0 0  1 1 1 0  1 0 0 0  1 0 0 0  1 0 0 0  1 0 0 0 ] def
/font8 [ 0 0 0 0  1 1 1 0  1 0 1 0  1 1 1 0  1 0 1 0  1 1 1 0 ] def
/font9 [ 0 0 0 0  1 1 1 0  1 0 1 0  1 1 1 0  1 0 0 0  1 1 1 0 ] def
/fontP [ 0 0 0 0  1 1 1 0  1 0 1 0  1 1 1 0  0 0 1 0  0 0 1 0 ] def
/fonti [ 0 0 0 0  0 0 0 0  0 0 1 0  0 0 0 0  0 0 1 0  0 0 1 0 ] def
/fonteq  [ 0 0 0 0  0 0 0 0  0 1 1 1  0 0 0 0  0 1 1 1  0 0 0 0 ] def
/fontdot [ 0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 0  0 0 1 0 ] def
/numar [ font0 font1 font2 font3 font4 font5 font6 font7 font8 font9 ] def

/crlf { currentpoint exch pop topx exch dotsize 6 mul sub moveto } def

/prnum {
  /str exch def
  0 1 str length 1 sub {
    str exch get
    48 sub numar exch get drawchar
  } for
  crlf
} def

図形を配列で与えているが、手続きdrawchar (左下→右上の順に描く) ではスタックにぶちまけているので逆順になる。逆順に描画するとかスタックを反転させるとかが面倒なので、データそのものを右上→左下の順で作った(笑)。

本体

0 0 0 setrgbcolor
toppage
fontP drawchar
fonti drawchar
fonteq drawchar
font3 drawchar
fontdot drawchar
crlf
(1415926... ) prnum
(8214808... ) prnum
...

1ページ目は、頭のPi=3.<改行>を1文字ずつプリントしている。

この部分が印字したいページによって異なる。したがって、この手前までをヘッダに作っておき、円周率生データテキストを適当な部分から適当な文字数ずつ切り取って ((...)内の文字列が1行になり、改行される) 適当な行数並べたもの (このコンバータはちょろっとCで書いた。省略) と、結合 (ここはシェルスクリプトを書いた) すれば、1ページ分のPSファイルが出来上がりである。

使ったプリンタ

さてこれを実行させたのは、職場のとあるXerox Docuprint C4000dという機械である。10年前くらいの機種でまあ古いんだけど、Postscriptで比較的わしが勝手に使えて、分何十枚かの高速で、大量に出してもお金が掛からない (←\pi 100億桁を2500枚くらい出力するのを夢見ている) 条件にある。UNIX系 (macOSとか) からは、PSファイルをlprすればそのまま出てくる。

カタログスペックでは、横2400dpi × 縦1200dpi、ということになっている。だがまあ信用していないので、600dpiで出力してみることにした。

上記の1ページ百万桁 (1000文字 × 1000行)PSファイルは、まずmacOS付属の『プレビュー』でプレビューしてみた。こいつはPS・EPSを読んで表示させることができる。……遅い。表示に30秒ほど掛かる。このマシンでChudnovskyの公式で\piを計算したら、とっくに100万桁の計算なぞ終わっている時間である。

画面表示は当然、ページの真ん中に灰色のベタ塗りが表示されているだけだが、こいつは拡大することができる。拡大して……も、しばらく掛かって当該部分の描画を始める。

この辺で胸に一抹の不安がよぎる。ページ……重すぎ?

さてプリンタに送ってみると

1分くらい考え込んで、いきなりのエラーである。

デバッグ用に作った、解像度1/10 (60dpi)・文字の大きさ10倍ファイルサイズ1/100のファイルを送ってみると、こちらは正常にプリントアウトできる。ということは、やはりプリンタのメモリが足りない。

macOS付属『プレビュー』では、他形式で書き出すことができるので、試しにPNGファイルに変換してみた。変換の解像度指定は、やはり同じ600dpiでも (『プレビュー』が考える1ドットの位置と、Postscriptプログラムが描く1ドットの位置が異なるので) アンチエイリアスが掛かっているようにみえる。

1200dpiで書き出すと、『プレビュー』上ではややまともになったように見える。

改めてPNGファイルを『プレビュー』で開き、macOS用のプリンタドライバ経由で (つまり画像として) プリンタに送ってみた。

600dpi・1200dpiのどちらを拡大してみても (わしは老眼・乱視の前に極度の近眼なので、コンタクトレンズをずらすと『マクロレンズ』状態になって、0.1mm以下のものもよくみえる。棟方志功が掘るときにやってるアレである)、文字らしきものは見えない。やはり、画像をプリンタ内部のバッファにレンダリングするときに、アンチエイリアスされて、さらに中間色はディザされているようである。

帰宅して、Micro Nikkor 55mm F3.5 Auto (ちなみにわたしより年上 (たぶん) の、クラシックレンズである。昔コピー機がない時代は、こゆので文献を複写した) をNikon Dfに取り付けて撮影してみる。

やはり、文字はアンチエイリアスされてディザされて、ウエストポーチの中で溶けて固まったチョコのように別のものになっているようである。それでも雰囲気でわかる1文字あたり横3ドット (ディザリングのドット) くらいにみえるので、どうやらプリンタ内部のパレットは400dpi相当くらい、と推測できる。

PNGにしてみる

ということでまた翌週の仕事の合間に、今度ははじめから無圧縮 (可逆圧縮) のPNGを生成して、同じプリンタに送って、『白黒2値画像のプリントアウトならどの解像度まで読めるか』を試してみることにした。

Python3 + OpenCV4でプログラムを書いてみる。今度は、プリント領域を決め打ちして解像度を指定 (桁数も指定できるが0にすると1ページに入るだけ出す) するようにしてみた。

PNGファイルは固有の大きさというものを持っていない (? メタデータにあるかも) ので、とにかくOpenCVの配列にドット絵を描き、macOS付属の『プレビュー』からプリンタドライバの『ページいっぱいに拡大』して出力する。このときの『ページいっぱい』を実測してみると (A4) 188 × 283mm であった。

Python3 + OpenCVでPNGお絵描き (プログラムリスト)

諸定数とフォントの定義

import numpy as np
import cv2 as cv
import sys

#pagewidth  = 177 / 25.4  # inch
pagewidth  = 188 / 25.4  # inch
#pageheight = 269 / 25.4  # inch
pageheight = 283 / 25.4  # inch

fontx = 3
fonty = 5
fontpitch = 4
baselineskip = 6

fontdic = {
    'P':'111101111100100',
    'i':'000100000100100',
    '=':'000111000111000',
    '.':'000000000000100',
    '0':'111101101101111',
    '1':'001001001001001',
    '2':'111001111100111',
    '3':'111001111001111',
    '4':'101101111001001',
    '5':'111100111001111',
    '6':'111100111101111',
    '7':'111001001001001',
    '8':'111101111101111',
    '9':'111101111001111',
}

今度は、画面の座標系は左上→右下、フォントの定義もその順番に並べている。

諸手続き

def drawchar(ch):
    global cy, cx, imbw
    if (ch in fontdic):
        ar = np.array(list(map(int, list(fontdic[ch])))).\
                      reshape((fonty, fontx))
#        print(ar, cy, cx)
        imbw[cy + (baselineskip - fonty):cy + baselineskip, cx:cx + fontx] =\
            255 - ar * 255
        cx += fontpitch
        return True
    else:
        return False

def crlf():
    global cy, cx, linch, lincnt
    cy = cy + baselineskip
    cx = 0
    linch = 0
    if cy < areay - baselineskip:
        return True
    else:
        return False

drawchar()は、描きたい文字が辞書fontdicにあれば、arに3 × 5で0/1の整数で並べて、白黒numpy配列 (白255または黒0をとるnumpy.uint8) のカーソル位置imbw[cy + 1:cy + 6, cx:cx + 3]にずっぽりbroadcastしている。

main

limitdig = 0
if len(sys.argv) != 4:
    print('usage: pibin_pngprint.py DPI PIFILE.txt OUTFILE.png',
          file = sys.stderr)
    sys.exit(1)
dpi = int(sys.argv[1])
srcfile = sys.argv[2]
imgfile = sys.argv[3]

areax = int(pagewidth * dpi)
areay = int(pageheight * dpi)
print(areax, 'x', areay, 'dots')
print(areax // fontpitch, 'chars,', areay // baselineskip, 'lines',
      file = sys.stderr)

imbw = np.ones((areay, areax), dtype = int) * 255
cx = 0
cy = 0

mode = 'r'
with open(srcfile, mode) as fp:
    fp.readline()
    for ch in list('Pi=3.'):
        drawchar(ch)
    crlf()
    nch = 0
    while limitdig == 0 or nch < limitdig:
        ch = fp.read(1)
        if not ch:
            break
        if drawchar(ch):
            nch = nch + 1
            print(nch, end = ' \r', file = sys.stderr)
            if areax - fontpitch <= cx and not crlf():
                break
    print('written', nch, 'digs', file = sys.stderr)
img = np.stack((imbw, imbw, imbw), axis = 2).astype(np.uint8)
cv.imwrite(imgfile, img)
cv.imshow('test', img)
cv.waitKey()

いちおうデバッグのために、OpenCVのimshow()で画面表示もしてみているが、なぞのもやもやが表示されるだけである。

円周率ファイルは、頭の1行が『Pi = 3.』となっているので、1行だけ読み飛ばして、別に『Pi=3.』を描画している (続けて出力したいなら別に読み飛ばさなくてもよい)。なお改行や空白などは (フォントで定義していない文字は全て) 読み飛ばしているので、てきとーにpretty printしてあるファイルでも入力に使える。

さて、600dpiと60dpi。これは、ほとんどPostscriptのときと変わりがない見栄えである。

400dpiではまだ潰れて文字が読めない。

200dpi・240dpiにしてみると、なんと。遠目でなぞの模様がページに出来上がる。

これは近づいてみてみると、 プリンタ内部のパレットのドットをまたぐ位置に描画されるプログラム上のドットは線が太くなり、両者が割と合っているところは線が細くなっている (のだろう。知らんけど)。

プリンタ内部の解像度とプログラム上の解像度 (空間周波数) がわずかにずれているとこれ (うなり) が周期的に起こる。いわゆるモアレ現象である。ちょっと驚いたのは、両者がずれているエッジでもアンチエイリアスされずそのままの濃さで出てくるということである。

以下試した解像度の一覧である。

解像度 文字数 クォリティ
60dpi 111 x 111
200dpi 370 x 371 モアレ
240dpi 444 x 445 モアレ
300dpi 555 x 557
320dpi 592 x 594
360dpi 666 x 668 ×
400dpi 740 x 742 ×
600dpi 1111 x 1114 ×

文字数はびっしり四角形に数字を並べた場合である (1ページめの『Pi=3.』だけの行があるところは、その分少なくなる)。

いろいろ追い詰めて、プリンタ内部の解像度はどうやら300dpi〜320dpiくらい、ということがわかった。1200の約数dpiなのかと思って上記のような解像度を試しているわけだが、320dpiがモアレも発生せずいちばん綺麗に見えた。

320dpiでは、1ページあたり約35万文字、100億桁出力するのに28437ページ・14218枚、再生コピー用紙にして積み上げると128cmほどになる、ということである。う〜ん? 当初の目論見と違うぞ?

2進で出力

こちらに経緯を書いたとおり、2進数で出力してみました。とはいっても、Pythonスクリプトはほとんど同じ。共用してみました。

PythonでPNGで砂嵐描き (プログラムリスト)

mainは全部掲載するが、サブルーチンは前のに追加、定数定義等前のと同じ。

諸手続き

def drawbit(bit):
    global cy, cx, imbw
    imbw[cy, cx] = 255 - bit * 255
    cx += 1
    return


def drawbyte(byt):
    mask = 0x80
    while mask != 0:
        drawbit(byt & mask != 0)
        mask >>= 1
    return

def bitcrlf():
    global cy, cx, linch, lincnt
    cy = cy + 1
    cx = 0
    if cy < areay:
        return True
    else:
        return False

1ビット描画・8ビット分描画と、1ドットの復帰改行 (古い……)。

main (めんどくさいので全部)

binaryprint = True
limitdig = 0
if len(sys.argv) != 4:
    if binaryprint:
        print('usage: pibin_pngprint.py DPI PIFILE.bin OUTFILE.png',
          file = sys.stderr)
    else:
        print('usage: pibin_pngprint.py DPI PIFILE.txt OUTFILE.png',
          file = sys.stderr)
    sys.exit(1)
dpi = int(sys.argv[1])
srcfile = sys.argv[2]
imgfile = sys.argv[3]

areax = int(pagewidth * dpi)
areay = int(pageheight * dpi)
print(areax, 'x', areay, 'dots')
print(areax // fontpitch, 'chars,', areay // baselineskip, 'lines',
      file = sys.stderr)

imbw = np.ones((areay, areax), dtype = int) * 255
cx = 0
cy = 0

if binaryprint:
    mode = 'rb'
else:
    mode = 'r'
with open(srcfile, mode) as fp:
    if binaryprint:
        fp.read(1)
    else:
        fp.readline()
    for ch in list('Pi=3.'):
        drawchar(ch)
    crlf()
    nch = 0
    while limitdig == 0 or nch < limitdig:
        ch = fp.read(1)
        if not ch:
            break
        if binaryprint:
            drawbyte(int.from_bytes(ch, byteorder = 'big'))
            nch = nch + 1
            if areax - 8 <= cx and not bitcrlf():
                break
        else:
            if drawchar(ch):
                nch = nch + 1
                print(nch, end = ' \r', file = sys.stderr)
                if areax - fontpitch <= cx and not crlf():
                    break

    print('written', nch, 'digs', file = sys.stderr)
img = np.stack((imbw, imbw, imbw), axis = 2).astype(np.uint8)
cv.imwrite(imgfile, img)
cv.imshow('test', img)
cv.waitKey()

引数解析とかめんどかったので、binaryprint (Falseでテキスト、Trueでバイナリ) で決め打ちにしてあります。

わざわざ10進テキストで出力したものを2進バイナリに変換したものを出力。整数部の『Pi=3.』はわざわざ、文字で出してみました(笑)

出力 (『プレビュー』でみたPNGファイル)。上で追い込んだプリンタの限界、320dpiにしてみた。この大きさで2368 × 3565ドット、1049905byte (10進で約31.6万桁) 分入っている。ひゃっは〜。2進変換したものは4152410120byte (小数部のみ)あるはずなので、A4 4000ページ (2000枚)、再生紙の束にして18cm厚さくらいに収まることがわかる。

左上を拡大したもの。Pi=3.243f6a88...がよくわかりますね (※ わかんねーって)。

レーザ光出力してみる

さて。プリントアウトはそろそろ飽きてきた。灰色の紙束を製造しても、なんも楽しくない。

円周率100億桁をブラウズできるよう出力するためには、なんかもっとこう高精細な、人間が読むためではない出力装置が必要である。ホログラム? レーザ発振器と分光器・変調器と写真乾板を用意して……とかいろいろ妄想して調べていたら、ホログラム記録を真面目に研究したら次世代の (1枚1TBとかのリムーバブル) 記録デバイスができちゃうんだな。やっぱ。

と、こんなところ (手近) に『Blu-ray Disc』なんて装置とメディアのセットがあるではありませんか〜? さっそく、円周率100億桁・10進数テキストバージョン (10GB) と、2進数バイナリバージョン (4.15GB) をいっぺんに出力してみる。

なんと。25分ほどで軽々と入ってしまった上に、書き込んだ数列は合わせて直径92mmほどの円形である。まだ余裕がある。使ったのは手元にあった2層50GBのメディアだが、1層25GBのメディアなら、DAISOで110円 (税込み) である。2人で分ければ、55円ほどで円周率100億桁が出力できる!!

出力は螺旋状に配置されており、円周率を象徴しているかのようで、お洒落である。『円周率 デザイン』でぐぐると出てくるお洒落なTシャツとかに引けを取らないぞ!! しかも、光に透かすとなかなか綺麗だ。

高速・廉価・綺麗でお洒落だ。肉眼で読めないのが、ちょっと難点だが……(※ もーやけくそ。楽しいか? →わし)。

結論: プリンタの解像度は幻想

結局、円周率100億桁のブラウズは叶わないままである……が、貴重な知見は得られた。

ということである。まあこれはモニタでは、さんざ4Kだの8Kだの不必要なだまし商品で言われていることであるが、人間の目の最小識別角として2K程度のドット数以上はいらねー、っていうのと同じである。2K以上が識別できる距離まで近づくとこんどは、他の部分が見えなくなる。後から他の部分 (視野中央部の解像度の良い部分以外) を仔細に眺め回すような写真・絵画とか、ビデオのノンリニア編集やDTMみたいに壁面いっぱいに情報をぶちまけて頭を移動させながら必要な部分を見る用途でない限り、一般家庭でも映画館でも4Kとか不要なのである。

プリンタでも、ほぼ200dpi以上は不必要である。1/200inch == 0.127mmの隣り合った点を識別するほど近寄って見る用途など、白黒2値の文書にですら、ない。ってか大抵のひとは見えない。まして多値の画像であるところの写真などは (白黒2値の文書のフォントの縁ですら) たぶんアンチエイリアスしたら72dpiだって高精細にみえるだろう。まあディスプレイと違って、どんなプリンタでもトナーやインクの粒粒がある・なしの2値画像しか出せないので (写真方式とか昇華式とか一部中間色が出せる例外はある)、ディザリングするためにその何倍かの出力解像度は取っているだろうが。

まあ不必要に多画素数のデジカメとか超高解像度のディスプレイとかプリンタとかに騙されて金を払うひとが多いから、経済は回るわけなんだが(笑)。少なくとも文書のスキャン画像で300とか600dpiとかにする必要はありませんぜ。

というわけで、この項まだまだ続く (続くのかよ)。

Discussion