2011年5月18日水曜日

unix time

前回のblog以後, ユリウス日を年月日に変換するプログラムも書きたくなっている.

私の愛読書の1つ, E.M.Reingold, N. DershowitzのCalendrical Calculationsでその辺の計算法を見てみよう. ユリウス日は, Julian暦-4712年1月1日が起点だが, 本書のカレンダーの起点は, Gregorian暦を昔の方へ外挿した1年1月1日を1とし, それからのFixed Day Numberで表わす. この日数をR.D.(Rata Die, fixed dateのラテン語)という.

本書の関数は, 本文では不思議な構文で書いてあり, 付録にはCommon Lispによる実装があるが, Scheme風にすると,

(define gregorian-epoch 1)
(define (gregorian-leap? y)
(and (= (modulo y 4) 0)
(not (member (modulo y 400) '(100 200 300)))))

(define (fixed-from-gregorian y m d)
(+ gregorian-epoch -1 (* 365 (- y 1)) (quotient (- y 1) 4)
(- (quotient (- y 1) 100)) (quotient (- y 1) 400)
(quotient (- (* 367 m) 362) 12)
(cond ((<= m 2) 0)
((gregorian-leap? y) -1)
(else -2)) d))

(fixed-from-gregorian 1 1 1) => 1
(fixed-from-gregorian 2011 5 16) => 734273


この本では, 年内の日数と月の変換は, 2月も30日あるとして計算し, 補正する

(map (lambda (m) (quotient (- (* 367 m) 362) 12)) (a2b 1 13))
=> (0 31 61 92 122 153 183 214 245 275 306 336)

floorをとらないと, 以下のような値である.

(.417 31. 61.583 92.167 122.75 153.333 183.917 214.5
245.083 275.667 306.25 336.833)

隣り同士の差を取ってみると,

(map (lambda (a b) (- a b))
'(31 61 92 122 153 183 214 245 275 306 336)
'(0 31 61 92 122 153 183 214 245 275 306))
=>
(31 30 31 30 31 30 31 31 30 31 30)

補正は, 1月2月はこのまま, 3月から後はうるう年なら1を引き, 平年なら2を引く.

さて, Gregorian暦のy, m, dからfixed dateを得る最初の関数fixed-from-gregorianの解説である.

前年までの経過日数が必要なので, (- y 1)が頻出する.

(* 365 (- y 1)) ;前年までの平日の日数
(quotient (- y 1) 4) ;Julian暦のうるう日の日数
(- (quotient (- y 1) -100)) ;100年の倍数の年はうるうをやめる
(quotient (- y 1) 400) ;しかし400年の倍数なら, やはりうる
う年にする
(quotient (- (* 367 m) 362) 12) ;2月を30日と仮定して, m月
の前月までの日数
(cond ((<= m 2) 0) ;補正
((gregorian-leap? y) -1)
(else -2)) d)) ;dを足す.


最初のgregorian-epochは, 1年1月1日を1にするためである.
最後のテスト例のようにうまく行く.
予想通り, 逆は難しい. プログラムは以下のようだ.


(define (gregorian-year-from-fixed rd)
(let* ((d0 (- rd gregorian-epoch))
(n400 (quotient d0 146097))
(d1 (modulo d0 146097))
(n100 (quotient d1 36524))
(d2 (modulo d1 36524))
(n4 (quotient d2 1461))
(d3 (modulo d2 1461))
(n1 (quotient d3 365))
(y (+ (* 400 n400) (* 100 n100) (* 4 n4) n1)))
(if (or (= n100 4) (= n1 4)) y (+ y 1))))

(define (gregorian-from-fixed rd)
(let* ((y (gregorian-year-from-fixed rd))
(prior-days (- rd (fixed-from-gregorian y 1 1))
  (correction (cond ((< rd (fixed-from-gregorian y 3 1)) 0)
((gregorian-leap? y) 1
(else 2)))
(m (quotient (+ (* 12 (+ prior-days correction)) 373)
367))
(d (+ (- rd (fixed-from-gregorian y m 1)) 1)))
(list y m d)))

(gregorian-from-fixed 1) => (1 1 1)
(gregorian-from-fixed 734273) => (2011 5 16)

と定義しておき, (gregorian-year-from-fixed rd)
で rd に対する年 y を計算する.

(n400 (quotient d0 146097)) :d0に400年の日数が何回あるか見る. 146097は400年の日数.
(d1 (modulo d0 146097)) :その400年内の日数

(n100 (quotient d1 36524)) ;その日数に100年は何回あるか. しかし400年の100年の日数は, 最初の100年の先頭の00年はうるう年なので, 36525日あるから

(d2 (modulo d1 36524)) ;その100年内の日数
(n4 (quotient d2 1461)) ;その日数内の4年の数
(d3 (modulo d2 1461)) ;その4年内の日数
(n1 (quotient d3 365)) ;4年内の1年の数
(n1 (quotient d3 365)) ;4年内の1年の数

基数変換をやっているみたいに簡単なのに驚く. 400年の中の日数d1のいろいろな値から得られるyをみてみる.


(define (gregorian-year-from-fixed rd)
(let* ((n100 (quotient rd 36524))
(d2 (modulo d 36524))
(n4 (quotient d2 1461))
(d3 (modulo d2 1461))
(n1 (quotient d3 365))
(y (+ (* 100 n100) (* 4 n4) n1)))
(if (or (= n100 4) (= n1 4)) y (+ y 1))))

すると, 以下のようになっていることが分かる.

rd y rd y
0- 364 1 365 35794- 36158 99 365
365- 729 2 365 36159- 36523 100 365
730- 1094 3 365 36524- 36888 101 365
1095- 1460 4 366
1461- 1825 5 365 72318- 72682 199 365
1826- 2190 6 365 72683- 73047 200 365
2191- 2555 7 365 73088- 73412 201 365
2556- 2921 8 366
2922- 3286 9 365 108842-109206 299 365
3287- 3651 10 365 109207-109571 300 365
3652- 4016 11 365 109572-109936 301 365
4017- 4382 12 366
4383 4747 13 365 145001-145365 398 365
4748- 5112 14 365 145366-145730 399 365
5113- 5477 15 365 145731-146096 400 366
5478- 5843 16 366


0から勘定を始めるのが基本と思っている私なら, 下の図の左のような関数を書くところだが, 本書の流儀は違う. d1=0ならy=1が返る. d1=146096なら, y=400であった. なるほど! (図の太い横線は366日の年を示す. 横線の左の黒丸は閉区間, 右の白丸は開区間を示す.)




別の図を描くと下のようになる. つまり, 図で網掛けの例外を最後に置くので計算が簡単になっていたのだ.




それなら, 私の流儀でも, 400年紀末から逆に計算すればおなじわけだった.


ところで, これではまだfixedから, 年が得られただけである. 本番のプログラムはこれを下請けに使う(gregorian-from-fixed d)である. その解法はこうだ.

とりあえずこのrdの落ちるyを求める. 次のその年の1月1日のfixed dateを求め, prior-daysとする. その年の3月1日のfixed dateを求め, prior-daysがそれより小さければ, 補正は0, そうでなくて, うるう年なら補正は1, 平年なら補正は2である. 次に前に日数を計算した式で, 月を見つけ, その月の1日のfixed dateとの差から, 日が分かるのである.

fixed-dateを何回も使うが, それだけ分かりやすいアルゴリズムになっている.

こういうプログラムの解読もなかなか面白い.

2011年5月16日月曜日

unix time

unixには1970年1月1日正子(0時0分)からの延秒数を数えている32ビットの時計がある. 2038年1月19日にサインビットが立つといわれ, 2000年問題みたいになにか起きかもしれないが, 私は多分もうこの世にはいず, 状況を知ることはかなわぬ.

この時計の元は, MITのMulticsではないか. Multicsには, 1900年1月1日正子からのマイクロ秒を数える52ビット時計があった. マイクロは10-6だから, 20ビット程度であり, unixの32ビットに対して52ビットなのは分かる. 私がMITに滞在したのは, 1973年9 月から74年7月までだが, その時計のサインビットが立ったのは, その少し前のたしか5月だったとある院生から聞いた.

まず脱線して, それがいつだったか計算してみよう. 例の個人用電卓が活躍する. 251は2251799813685248. これを1日のマイクロ秒864000000000で割る.

商は26062, 剰余は43013685248. つまり1900年1月1日から26062日後を知りたい. それには1900年1月1日のユリウス日2415021に26062を足し, その2441083がユリウス日になる日を知ればよい.

ここから先は電卓から離れ, 理科年表のユリウス日の表による.

すると, 1971年5月11日がその日であることが判明. 日以下を計算すると, 11時 56分 53秒 685248マイクロ秒であった.

jdをユリウス日を計算する関数として, Schemeで検算すると,

(+ (* (- (jd 1971 5 11) (jd 1900 1 1)) 86400000000)
(* 11 3600000000) (* 56 60000000) (* 53 1000000) 685248)
=> 2251799813685248

(factorize 2251799813685248)
=>
(2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)

(length (factorize 2251799813685248)) => 51


本題へ戻り, 通常のy年mon月d日h時min分s秒からunix timeへの変換の方は, Multicsの場合と同様に簡単だ.

(JD(y,mon,d)-JD(1970,1,1))*86400+h*3600+min*60+s

となる.

一方, unix time tからy,mon,d,h,min,sへの変換は, ユリウス日からy,mon,dへの変換関数があればなんでもないが, まだそういうプログラムは書いたことがない.

カレンダーの変換で一番面倒なのは, Grogorian暦でのうるう年の計算である. しかし, ことunix timeに限れば, 2000年が通常のうるう年なのが幸いだ. そこで書いたのが次のSchemeのプログラムである.

(define (unixtime t)
(let* ((s (modulo t 60)) (m (quotient (modulo t 3600) 60))
(h (quotient (modulo t 86400) 3600)) (d (quotient t 86400))
(y (- (floor (/ (+ d 731) 365.25)) 2))
(c (- d (floor (+ (* y 365.25) 0.25))))
(e (if (= (modulo y 4) 2)
'(0 31 60 91 121 152 182 213 244 274 305 335)
'(0 31 59 90 120 151 181 212 243 273 304 334)))
(n (apply + (map (lambda (f) (if (<= f c) 1 0)) e))))
(list (inexact->exact (+ 1970 y)) n
(inexact->exact (- c (list-ref e (- n 1)) -1)) h m s)))


引数のtがunixtimeである. 最初にs(秒), m(分), h(時)を取り出す. dは通算の日数になる. その後の変数は, yが1970年以降の年数, cがその年内の日数, nが月だ. yはdを365で割ってfloorを取ればよいが, 閏年があるからそうは問屋が卸ろさない.

まず, y年について, 前の年の終りまでの日数は,



欲しい値は,



365の代りに365.25で割ればよさそうに見えるので, 2.25でテストしてみる.

(map (lambda (n) (floor (/ n 2.25))) (a2b 0 20))
=> (0 0. 0. 1. 1. 2. 2. 3. 3. 4. 4. 4. 5. 5. 6. 6. 7. 7.
8. 8.)

4が3個並ぶのがうるう年に対応し, これを2年にしたいから, 3つの0と2つの1の5個をスキップするために, nの代りに(+ n 5)とし, 最後に2を引く.

(map (lambda (n) (- (floor (/ (+ n 5) 2.25)) 2)) (a2b 0 20))
=> (0. 0. 1. 1. 2. 2. 2. 3. 3. 4. 4. 5. 5. 6. 6. 6. 7. 7.
8. 8.)

なるほどうまくいくので, 356.25に修正し, テストする.

(map (lambda (n) (- (floor (/ (+ n 366 365) 365.25)) 2))
'(0 364 365 729 730 1095 1096 1460 1461 1825 1826 2190
2191 2555))
=> (0. 0. 1. 1. 2. 2. 3. 3. 4. 4. 5. 5. 6. 6.)

うまくいく. これを1970に足せばよい.

次に上の表にあった前年までの日数の和を計算するには,

(lambda (y) (+ (* y 365) (quotient (+ y 1) 4)))

(lambda (y) (floor (+ (* y 365.25) 0.25)))

とする. これをdから引くと, その年内の日数cが得られる. 前の年の終りまでの日数の和のように, 前の月の終りまでの日数の和のリストを, うるう年か否かで変数eに用意する.

月nは, このリストで, cが越えるものの数として得る. nが決れば, 月内の日数は, cから先ほどのリストの要素を引いて作る.

これで完成. テストしてみる.

(unixtime 0) => (1970 1 1 0 0 0)
(unixtime (expt 2 31)) => (2038 1 19 3 14 8)

2011年5月10日火曜日

加算の順序

桁数が多い十進数でも, その筆算による足し算は小学校で習う. 最近のことは知らぬが, 昔は算盤も小学校で習った.

ところで, 筆算は下の桁から始め, 算盤は上の桁から足し始める. もちろん, 結果は同じになる. これは一体どういうことかと, 子供のころ不思議であった.

実は, 足し算(引き算もだが)は, 繰上げを正しく処理し, 全部の桁について加算するなら, どの順に桁を選んでやっても構わないのである.

算盤で 8 8 8 8 8 8 8 8 に 1 1 1 2 1 1 1 2 を足すとき, まず最初の3桁で,

8 8 8 に
1 1 1 を足すと
9 9 9 になる.

次に8に2を足すから, 10になるが, 算盤流では8を払ってその桁を0にしてから, 繰上げ処理に入る. つまり, 次の9に1を足す. それには9を払い, さらに繰上げを処理する. 3つの9から繰上げが続き, 9の上の桁に1がでて止る. 都合4回繰り上げた. 繰上げをピリオドで示し, これを 1.0.0.0.0 と表わす.

その次に3回9が得られ, 1.0.0.0.0 9 9 9 となり, 最後の8+2でまた繰上げが発生. 4回繰り上げて1.0.0.0.1.0.0.0.0 が得られる.

以上が算盤であった.

筆算だと右端の8+2から始め, この桁の結果が0, 繰上げがでる. 従って .0 となり, 繰上げがあったことを記憶して, 次の8+1に進む. この和は9だが, 繰上げがあったので, 0になって再び繰上げを記憶, 次へ進む. このようにして, .0.0.0.0 まで来た. 次は8+2なので, 和は10. それに繰上げで11になり, この桁が1となり, さらに繰上げが続く. こうして,1.0.0.0.1.0.0.0.0となる.

左右のどちらから計算しても, 繰上げの出る場所は決っている.

タイガーのような計算機ではどうか.

8 8 8 8 8 8 8 8 に
1 1 1 2 1 1 1 2 を足すと一旦
9 9 9.0 9 9 9.0 となり,

繰上げが発生した場所がマークされる. その後, 下の桁から繰上げセンサーがマークをスキャンし始める. マークがあると, 繰上げピンにより, 次の桁を1増やす. この結果繰上げが生じることがある. 今の場合もそうだ.

9 9 9.0 9 9.0.0 となり, スキャンは今つけたマークのところに来る. そして9を0にし, 次に桁との間にマークする. 繰上げで1増やそうとするとき, その桁が9でなければ繰上げ処理は終る. 0のところが 1.0.0.0.0 となるが, その次にすでにマークがあるので, 続く9を次々を0にして繰上げを伝える.

結局, 上の算盤や筆算と同じ場所で繰上げが生じ, 同じ結果になる.

足し算をランダムな順でやってみよう.

0 3 6 2 7 5 1 4 ← 足す順
1 1 1 2 1 1 1 2 ← 足す数
8 8 8 8 8 8 8 8 ← 元の数

まず順が0の桁で足す. 従って

0 3 6 2 7 5 1 4 ← 足す順
1 1 1 2 1 1 1 2 ← 足す数
9 8 8 8 8 8 8 8 ← 元の数

となる. 繰上げはない. 次に順が1の桁へ行く.

3 6 2 7 5 1 4 ← 足す順
1 1 2 1 1 1 2 ← 足す数
9 8 8 8 8 8 9 8 ← 元の数

順が2の桁へ行く

3 6 2 7 5 4 ← 足す順
1 1 2 1 1 2 ← 足す数
9 8 8.0 8 8 9 8 ← 元の数

と繰上げが生じたからその処理をして

3 6 2 7 5 4 ← 足す順
1 1 2 1 1 2 ← 足す数
9 8 9.0 8 8 9 8 ← 元の数

順が3の桁

3 6 7 5 4 ← 足す順
1 1 1 1 2 ← 足す数
9 9 9.0 8 8 9 8 ← 元の数

順が4の桁, 繰上げも処理する.

6 7 5 4 ← 足す順
1 1 1 2 ← 足す数
9 9 9.0 8 9.0.0 ← 元の数

順が5の桁

6 7 5 ← 足す順
1 1 1 ← 足す数
9 9 9.0 9.0.0.0 ← 元の数

順が6の桁

6 7 ← 足す順
1 1 ← 足す数
1.0.0.0.0 9.0.0.0 ← 元の数

順が7の桁
7 ← 足す順
1 ← 足す数
1.0.0.0.1.0.0.0.0 ← 元の数

これで, お勧めはしないが, どういう順に足してもよさそうだと分かる.

十進法の2つの数を足すとき, 各桁で加算し, 仮の和 0≤t<19を得る.

それと下の桁からの繰上げの有無により, その桁の真の和と, 上の桁への繰上げが得られる.


t<9なら, 繰上げのありなしに拘らず, 上の桁への繰上げは生じない. 真の和は, 繰上げがないならt, あるならt+1.

t=9で, 繰上げがないなら, 繰上げは生じず, 真の和は9. 繰上げがあるなら, 繰上げが生じ, 真の和は0.

10≤tなら, 繰上げのありなしに拘らず, 上の桁への繰上げが生じる. 真の和は, 繰上げがないならt-9, あるならt-10.

上の加算の例でいうと,

8 8 8 8 8 8 8 8
1 1 1 2 1 1 1 2
9 9 9 0 9 9 9 0 ← 仮の和
. . ← 繰上げ
0 0 ← 真の和
. . ← 繰上げ
0 0 ← 真の和
. . ← 繰上げ
0 0 ← 真の和
. . ← 繰上げ
1 1 ← 真の和

2011年4月29日金曜日

古い計算機

今回はPascalが発明したといわれる計算機, パスカリーヌの繰上げ処理のことを書きたい.

Pascalは1623年生まれ. 計算機を考えたのが1642年から45年だから, 20歳くらいのことになる.

そんなに高級なことが出来たわけではなく, 例によって歯車にスタイラスを指してぐるーっと回すと, その分だけ加算出来, 結果が表数車に現れるという代物である. 減算は補数を足して実現したようだ. つまり歯車類は, 一方向にだけ回転する構造であった.


ウェブページ
にあった図を一つ借用すると, 計算機構は下のようになっていたらしい.



図の 0 1 は表数車で, パスカリーヌは十進法以外にも対応していたらしいが, 一応は外側に0から9と, 内側にその補数が書いてあった. 2 は歯止めである. 歯車といっても, 精密な工作は出来ぬ時代なので, 円周から棒がたっている程度のもので, むかしのからくりでよく見かけるものがあり, 歯止めはそれを上から重力で押さえている.

図の 3 はベベルギアの片割れである. 下の方の図にも 3 はあるが, 表面の歯車をスタイラスで回すと, このベベルギアで水平面の軸の回転に変わる. この軸が1段の歯車を介して表数車を回す.

ところで繰上げは, 下の桁の水平軸の用意した2本のピン 4 が主役である. 軸が図でみて時計方向に回ると, このピンは, 上の桁の水平軸で回転する 5 のフォーク状のフックを押し上げ始める. やがて 4 5 の下から抜けるが, それがちょうど表数車が9から0になる時で, フックが重力で墜落することにより, 上の桁のピン 6 が押されて, 繰上げを生じる仕掛けである.

繰上げ処理には, 力がいるのが普通だが, パスカリーヌでは重力を利用するのが面白い. 表数車が5, 6, 7, 8と回るにつれ, ポテンシャルを高めていき, 9を過ぎると一気に落ちるのである.

私はこの仕掛けをみると, 昔のテレタイプを思い出す. テレタイプは1文字ずつ受信, 印字しながら, 印字位置が次第に右に移動し, 復帰信号を受信すると印字位置を左端へ戻すのだが, このエネルギーは, 印字位置を右に移動するときにためていく. 復帰でフックがはずれ, 蓄えたエネルギーで左端へ飛んで戻るが, 最後にダッシュポットがあって, ブレーキをかけ, 静止する. 従って, 左からほんの数文字を印字した後の復帰では, 力が弱く, 左端まで充分戻れないこともあった.

これは, 手動のタイプライタとは, 全く反対のエネルギーの使い方であった. 手動のタイプライタでは, 手でプラテンを右に寄せると, その時にエネルギーを蓄えるのであった. 1文字打つたびに, そのエネルギーにより, プラテンが左へ送られるのである.

パスカリーヌのこの動きを, いつものようにポストスクリプトで描いたのが次の紙芝居である. 4 あたりから, ピンがフックを押し上げ始め, 9 で最高点に達する. その直後にフックは墜落し, 上の桁のピンを回す.



いかにもうまく行きそうだが, 繰上げが続くと, 2, 3段で駄目になったという説明を読んだようにも思う. なお上の図の動画がhttp://playground.iijlab.net/~ew/pascalinecarry/pascalinecarry.htmlにある.

2011年4月5日火曜日

八進法算盤

以前このブログに八進法算盤のことを書いた(2008年9月14日). 今回もその話題だ.

前回のブログを書いたころ, 八進法の算盤は私のが最初だろうと思っていた. しかし, 東京理科大学の近代科学資料館で蒐集した算盤群を眺めて目をこする.



手前にあるのは下が3珠の算盤ではないか! これは八進法用なのか. 説明がないから目的は皆目不明である. この算盤の奥には, なんやら二進法の算盤らしいものさえある. 故事来歴を知りたいところである.

ところで...
子供のころ, 学校で算盤を習った. もちろん十進法の算盤である. すこし使い方を覚えると, 1+2+...+10とかやってみたくなる. 答が10までなら55, 100までなら5050なのは, 周知のとうり.

自作の八進法の算盤でも, 1から八進法の10まで足してみると, 上の(天の)珠が並び, 44になって驚く. 同じパターンだ. これは偶然であろうか. たしかに1+2+...+8=36. これは4*8+4だから44なわけだ.

Gaussが子供のころ, 1+2+...+100を計算するのに, (1+100)+(2+99)+...とやった逸話は有名である.

その伝でいくと, 十進法で1から10まで足すのは, まず10を除外し, 1+9, 2+8,...を作るとそれらも10で, 4通り出来る. 5は継子になる. 合計55の1の桁の5は, その継子の5である. 4通りの10と, 最初に除外した10と合わせると10も5組になり, 55になる. この推論はこのまま八進法に適応出来る. なーんだという気分. 八進法の100までの和も4040だ.

もっと一般的にやろう. 2n進法で1から2nまで足すと, 2n(2n+1)/2=n(2n+1)だから, 2nの桁も1の桁もnになるのだね.

Gaussは子供のころ家が貧しかったせいか, こういう神童的な話が伝わる. Gaussが自分で復活祭の日取りを計算する式を考え出したのは, 母親がGaussの誕生日もろくに覚えていないような人だったかららしい.

Gaussといえば, 高木先生の解析概論に, 「Wolframの表には1000以下の素数の自然対数の50桁の表が掲げられてゐる. この表は既に少年がうすが愛用したものである.」という脚注がある. どんな表なのか興味はある. そのWolframはMathematicaのWolfram Researchとは関係ないはずだが, おなじWolframなのも何かの因縁であろう.

さて, iPod touch用に私が開発した電卓は, 八進法, 十進法, 十六進法の各基数で計算が出来る. 基数を十進法に設定し, 10 Ent 11 * 2 /とすると55; 八進法, 十六進法で全く同じ入力をすると, 44と88になり, 当然とはいえ, 楽しい.

2011年4月4日月曜日

古い計算機

前回のブログのDial-A-Maticの計算機の特許の図で, その上方にある歯止めの機能を理解したいと思い, ひがな一日PostScritpで描いた図をこねくりまわした.

すなわち, ダイアルと伝達歯車と歯止めを表示し, パラメータを変えて, ダイアルを少しずつ回転しながら, この点とこの点が接しているはずと, 伝達歯車や歯止めの回転角度を繰り返し調整した.

ダイアルが加算方向に右回転し, 状態が9から0になって繰上げが出る過程の最初が0番の図で, それから以下の紙芝居, あるいはパラパラ漫画は始まる. それぞれの図の右上の番号nは, ダイアルの0番からの回転角が3n度であることを示す.

鳥のようにも見える歯止めには, 左と右に下向きの三角がある. 左のをストッパー, 右のをフォロワーといおう.



最初の0番では, 歯止めは伝達歯車の2本のピンの間をしっかり押さえている. 1番の図に向って, ダイアルの右回転を始めると, ダイアルの突起が伝達歯車のピンを押し, 伝達歯車が左回転し始める. それにより歯止めのストッパーの斜辺が, ピンで上に押され, 歯止めは右に傾く. 1番以降, 赤線は0番の位置を示す.


2番と3番では, ダイアル, 伝達歯車, 歯止めの回転が継続中.



4番. 伝達歯車のピンによるストッパーの押し上げはここまで. つまり歯止めの右回転は終り. この後しばらく, フォロワーはダイアルの0の数字の上の谷の部分を移動する.



6番. 18度回転したので, 状態9と0との中間点に来た.



9番から10番にかけて, フォロワーは0の数字の左の斜面に当り, 押し上げられ, 左回転を始める. 同時にストッパーが, 次のピンの間に下がり始める.





12番でダイアルは36度回転し, 状態0になる. しかし, ストッパーはピンの間に充分には下がっていない.



13, 14, 15番で, 状態が0からさらに1へと変る時, 数字1の方にある高い山の斜面でフォロワーが押し上げられ, ストッパーが伝達歯車のピンの間にしっかり止る.

フォロワーを押し上げる斜面が, なぜ2つに分かれているかは, 残念ながら未解決である. また, 歯止めは, ダイアルの急回転により, 繰上げが2回生じるのを防ぐのが目的なのだが, 8番の図で, ストッパーが上がっている間に, もう1本のピンもストッパーの下を通過しそうにも思える. その辺りも解明出来ていない.

前回も引用した, 繰上げ処理に詳しいウェブ の最後の方に, この計算機の内部の写真が2つある. 特許の図の上の写真は, 蓋を取ったものであり, 最後のは, 教育用に蓋を透明にしたものだ. これ, 欲しいなぁ.

2011年4月2日土曜日

古い計算機

このブログに何回か書いた昔の計算機は, タイガー計算器のような乗除算は出来ないが, 簡単な加減算ならお手の物だ. そういう計算機をウェブページで探していたら, Sterling Dial-A-Maticというのを見つけた. これが理科大の近代科学資料館にあったかどうかは分からぬ.

John Wolffさんのウェブページには, 1950年代かとある.



この画像を頂いたもとのURLが思い出せないのが申し訳ない.

プラスティック製だから, 古いとはいえない. 古いのと同じく簡単なのである. 簡単なので, 筆箱に蓋に組込める. この計算機を使うにはスタイラスがいるが, スタイラスが筆箱に仕舞えるのも都合がいい.

この計算機は十進4桁. 右からunits(一の桁), tens(十の桁), hundreds(百の桁), thousands(千の桁)である. それぞれの桁にnを足すには, 周囲に書いてある外側の大きい数字のnのところの穴にスタイラスを差し, 馬蹄形の最後まで右に回す.

nを引くには, 内側に書いてある小さい数字のnの穴にスタイラスを差し, 左に回す.

すると上の穴にその桁の答が現れる.

予想されるように, 繰上げ, 繰下げは正しく反映される.

2桁だけを書き出すと次の図だ.



中が気になるが, 繰上げ処理に詳しいウェブ にこの計算機の説明があり, そこからUS Patent 2,797,047に辿り着ける. するとこういう図を始めいろいろ見つかる.



特許の図だからごちゃごちゃしているし, 説明も回りくどい. 今回はそれを私なりに説明したい. 十進歯車の相当する円盤の上方にカムのような切り込みがあり, それに噛み合う上側の歯止めや下側のバネみたいなものは, 回転しすぎ防止と位置の調整用のものなので省略し, 特許の図の繰上げに本質的な部分を描くとこうなる.



この赤で示した蓋を外すと



になる. 右が一の桁のダイアル, 左が十の桁のダイアルで, その状態が真上にある数字で読めるように, 数字は放射状に書いてある.

中央に繰上げ用の伝達歯車があり, 歯は十の桁のダイアルの下の歯車と噛み合っている. 伝達歯車には, 緑で示す10本のピンが立っていて, またダイアルの2の数字の下あたりに, これも緑で示す突起とぶつかる高さになっている.

ダイアルが36度左回転して, 0から9の状態に変ると, 突起はピンに当って, 伝達歯車を36度右回転させる仕掛けである.

ここが繰上げ機構の微妙なところで, 一の桁のダイアルの繰上げは, 伝達歯車に伝わるが, 伝達歯車の回転は一の桁の突起に当ってはいけないのである. 従って, 突起はピンの外側の包絡円のすぐ外側に控えている.

この構造で伝達歯車と十の桁のダイアルを36度ピッタリに回すのはむずかしく, 従って, 特許の図では, 針金のバネでダイアルの下の歯車を, 定位置に止めようとする.

もう一つ微妙なのは, 一の桁のダイアルを, 勢い良く回転して, 繰上げを伝えた場合である. 突起がピンを急に押すと, 36度どころか, 72度まで回転してしまうかも知れない. これを防ぐのが, ダイアルの上の切り込みと歯止めである.

歯止めの動きはまだよく理解してはいないが, そういう機能である.

私がProcessingで描いたシミュレータが,
http://playground.iijlab.net/~ew/dialamatic/dialamatic.html
にある.

起動時には, 0 0 の状態である. 一の桁に1を足すには, 一のダイアルの大きい数字の横の円をクリックすると, 青色に変る. 右下の + の箱をクリックすると1が足される仕掛けだ. 引くには左下の - の箱をクリックする.

クリアの機能はない.