パイこね変換

「渋滞学」という本を読んでなかなか面白かった。その中で、「パイこね変換」というのが、「コンピュータが簡単な計算でも間違える例」として紹介されていた。

http://tftf-sawaki.cocolog-nifty.com/blog/2006/11/post_b8e3.html

(*)本書に出てくる「パイこね変換」の例
 (1) まず0から1までの範囲の数xを決める
 (2) xが0.5よりも小さければ y=2xとし、そうでなければ y=2-2xとする
 (3) 求めたyをxに代入し、(2)に戻って計算を繰り返す
 例えば最初に x=0.1とすると、本来は0.4と0.8でずっと振動するはずが、数十回の計算で0になってしまう

Javaで書くとこんな感じ


public class Pankone {
public static void main(String[] args) {
double num = 0.1;

for(int i=0; i<1000; i++){
num = calc(num);
System.out.println(i + "回目:" + num);
if(num == 0){
break;
}
}
}

static double calc(double num){
if(num > 0.5){
return 2 - 2 * num;
}else{
return 2 * num;
}
}
}

実行結果。たしかに100回もやらずに0になる。

0回目:0.2
1回目:0.4
2回目:0.8
3回目:0.3999999999999999
4回目:0.7999999999999998
5回目:0.40000000000000036
6回目:0.8000000000000007
...(中略)
50回目:0.8125
51回目:0.375
52回目:0.75
53回目:0.5
54回目:1.0
55回目:0.0

ちゅうか、それって単に浮動小数点の丸め誤差の問題じゃないか。
例えば10倍して整数にしてやると、


public class Pankone2 {
final static long MP = 10;
public static void main(String[] args) {
long num = (long)(0.1 * MP);

for(int i=0; i<1000; i++){
num = calc(num);
System.out.println(i + "回目:" + num);
if(num == 0){
break;
}
}

}

static long calc(long num){
if(num > 0.5 * MP){
return 2*MP - 2 * num;
}else{
return 2 * num;
}
}
}

ちゃんと、4と8を繰り返すよー

0回目:2
1回目:4
2回目:8
3回目:4
4回目:8
...(以下略)

それはコンピュータが計算を正しく行えないのでなく、弱点をついて変な計算をやらしてるだけ?ある意味

int a = (int)0.5

が0になるコンピュータなんて!みたいな。


と、書いてみて、「コンピュータは素早く正確に計算するもんだ」という一般常識に「必ずしもそうでない場合もある」というという例を示しているだけのとこに、しょうもないつっこみをしたなー、という気もしたりして。


いや、なんかモンティーホールパラドックスみたいな驚きを求めていたのだが、いまいちだった。。。