久しぶりにラングレーの問題について書いてみる。
ラングレーの問題、フランクリンの凧、整角四角形問題、などなど、いろいろな名前で呼ばれている。
簡単に説明すると、
ある凸四角形があり、
それぞれの頂点を、反時計回りに左上からA、B、C、Dとし、
AC、DC、の対角線を結んだ図形において、
底辺BCとした下方に出来る4角、
∠ABD=α˚
∠DBC=β˚
∠ACB=γ˚
∠ACD=δ˚
とし、これらの値が度数法で整数となる値が与えられ、
∠ADB=θ˚
を求める問題。
α=20、β=60、γ=50、δ=30のものを、ラングレーの問題と呼んだりする。
凸四角形と対角線で凧に見えることから、フランクリンの凧、
与えられた角および、求める角がすべて、整数の角になることから、整角四角形問題、
などと言う。
これらの問題は、初等幾何学、代数学の範疇で解かなければダメという縛りが存在する。
簡単に言えば、解析学的に解いてはダメということです。
前回の記事は、
こんな感じ。
今回は、前回の記事で、整角整数問題の問題数は、1002733個ということを、プログラムで数え上げたと書いた。
その辺について、サンプルを交えて詳しく書いてみようかと思う。
まず、解くのに解析学は使えないが、何個あるのかを数えるのには使っても良いだろうと考える。
何を使うかといえば、逆三角関数である。
なお、プログラミングにα、β、γ、δ、θは変数として使えないので、それぞれ、a、b、c、d、xとする。
例えば、Javascriptならば、
var deg=180.0/Math.PI;
var rad=Math.PI/180.0;
var e = Math.tan(c*rad)/(Math.tan((a+b)*rad)+Math.tan(c*rad));
var f = Math.tan(b*rad)/(Math.tan((d+c)*rad)+Math.tan(b*rad));
var g = e*Math.tan((a+b)*rad);
var h = f*Math.tan((d+c)*rad);
var x = b-Math.atan2(h-g,1-e-f)*deg;
例えば、Excelならば、
A1セルにa、B1セルにb、C1セルにc、D1セルにdの値が入っていたとして、
E1セルに、
=TAN(RADIANS(C1))/(TAN(RADIANS(A1+B1))+TAN(RADIANS(C1)))
F1セルに、
=TAN(RADIANS(B1))/(TAN(RADIANS(C1+D1))+TAN(RADIANS(B1)))
G1セルに、
=E1*TAN(RADIANS(A1+B1))
H1セルに、
=F1*TAN(RADIANS(C1+D1))
I1セルにxの値を求めるため、
=B1-DEGREES(ATAN2(1-E1-F1,H1-G1))
どちらにしても、xをatan2、数学で言うところのarctan、逆正接関数(アークタンジェント)を使って求めている。
つまり、逆三角関数を使えば、α、β、γ、δの取り得る値であれば、θを求めることが出来るということでもある。
※因みに、関数としてatanではなく、atan2を使ったのは、分母が0になる可能性があるからです。
とは書いたものの、プログラミング言語で計算するわけであるから、三角関数や逆三角関数といった小数を扱う関数において、誤差というものを考えなければならない。
小数点以下に0が何個続けば、整数とみなすのか。
はたまた、小数点以下に9が何個続けば、繰り上げて整数とみなすのか。
ということを考えなければならないだろう。
とりあえず、この問題は一旦置いておいて、いったいどれくらいの母数があるのかを探ってみる。
もしくは、
といったネスト(入れ子)になる。
因みに、このループの回数を数えると、83653681回となります。
a、b、c、dは整数を入れても、かならずxが整数となるかは解らないので、このループ回数が、整角四角形問題の総数ではないことは、明らかである。
そこで、以下のようなJavascriptのプログラムを考えてみた。
簡単に説明すると、
arctanで求めた小数のままのθ(プログラムではx)の値と、
整数化したθ(プログラムではy)の値との差の絶対値を、
log10を取ったもので分布してカウントする。
α=δ、すなわち円に内接する、
α≠δ、すなわち円に内接しない、
も同様に別途カウントする。
出力結果は、このようになった。
まず、円に内接するケースに着目すると、円に内接するすなわち、α=δより、円周角よりθ=γとなり、整角四角形問題となることは明白である。
故に、円に内接するの分布域は、すべて誤差の範疇と言える。
円に内接しないケースも同様な分布域、もしくは多少精度の悪い分布域を示す可能性はあったが、10^-nの7~9に明らかなフタコブラクダの谷間が出来ており、同様に8以降は誤差の範疇と考えるに至る。
これより、
整角四角形問題の総数は、
314+6746+75134+433139+213083+5756+268561=1002733
円に内接するものは、
308+6594+71925+407665+197280+5604+250553=939929
円に内接しないものは、
6+152+3209+25474+15803+152+18008=62804
という個数が求まったのである。
同様のことを、C言語でやってみたが、結果は同じようにフタコブラクダの谷間ができ、誤差と捉えた部分の分布は違っても、上記の合計は全て等しかった。
ただ、プログラミング言語においても、フタコブラクダの谷間が0となるかは、関数や変数の精度次第だろう。
さて、プログラミングはここまでとし、数学にもどる。
ラングレーの問題、フランクリンの凧、整角四角形問題、
いずれにしても、初等的な逆三角関数なら使ったとしても、解析的な逆三角関数を使わずに解くということが、この問題の醍醐味である。
どれだけ単純で簡単な方法で導くかというところの勝負でもある。
本当に1002733個すべてを分類して、すべてにおいて初等幾何学、代数学の範疇で答えを導き出せたのだろうか。
これらの書籍を読めば解るのだろうか。
まぁ、1002733という総数とラングレーの問題等とを紐付けしてネットで検索しても、みつかるのは、こののブログくらいなものだろう。
さて、Owndにでもプログラムを書こうかな。
ではでは
ラングレーの問題、フランクリンの凧、整角四角形問題、などなど、いろいろな名前で呼ばれている。
簡単に説明すると、
ある凸四角形があり、
それぞれの頂点を、反時計回りに左上からA、B、C、Dとし、
AC、DC、の対角線を結んだ図形において、
底辺BCとした下方に出来る4角、
∠ABD=α˚
∠DBC=β˚
∠ACB=γ˚
∠ACD=δ˚
とし、これらの値が度数法で整数となる値が与えられ、
∠ADB=θ˚
を求める問題。
α=20、β=60、γ=50、δ=30のものを、ラングレーの問題と呼んだりする。
凸四角形と対角線で凧に見えることから、フランクリンの凧、
与えられた角および、求める角がすべて、整数の角になることから、整角四角形問題、
などと言う。
これらの問題は、初等幾何学、代数学の範疇で解かなければダメという縛りが存在する。
簡単に言えば、解析学的に解いてはダメということです。
前回の記事は、
こんな感じ。
今回は、前回の記事で、整角整数問題の問題数は、1002733個ということを、プログラムで数え上げたと書いた。
その辺について、サンプルを交えて詳しく書いてみようかと思う。
まず、解くのに解析学は使えないが、何個あるのかを数えるのには使っても良いだろうと考える。
何を使うかといえば、逆三角関数である。
なお、プログラミングにα、β、γ、δ、θは変数として使えないので、それぞれ、a、b、c、d、xとする。
例えば、Javascriptならば、
var deg=180.0/Math.PI;
var rad=Math.PI/180.0;
var e = Math.tan(c*rad)/(Math.tan((a+b)*rad)+Math.tan(c*rad));
var f = Math.tan(b*rad)/(Math.tan((d+c)*rad)+Math.tan(b*rad));
var g = e*Math.tan((a+b)*rad);
var h = f*Math.tan((d+c)*rad);
var x = b-Math.atan2(h-g,1-e-f)*deg;
例えば、Excelならば、
A1セルにa、B1セルにb、C1セルにc、D1セルにdの値が入っていたとして、
E1セルに、
=TAN(RADIANS(C1))/(TAN(RADIANS(A1+B1))+TAN(RADIANS(C1)))
F1セルに、
=TAN(RADIANS(B1))/(TAN(RADIANS(C1+D1))+TAN(RADIANS(B1)))
G1セルに、
=E1*TAN(RADIANS(A1+B1))
H1セルに、
=F1*TAN(RADIANS(C1+D1))
I1セルにxの値を求めるため、
=B1-DEGREES(ATAN2(1-E1-F1,H1-G1))
どちらにしても、xをatan2、数学で言うところのarctan、逆正接関数(アークタンジェント)を使って求めている。
つまり、逆三角関数を使えば、α、β、γ、δの取り得る値であれば、θを求めることが出来るということでもある。
※因みに、関数としてatanではなく、atan2を使ったのは、分母が0になる可能性があるからです。
とは書いたものの、プログラミング言語で計算するわけであるから、三角関数や逆三角関数といった小数を扱う関数において、誤差というものを考えなければならない。
小数点以下に0が何個続けば、整数とみなすのか。
はたまた、小数点以下に9が何個続けば、繰り上げて整数とみなすのか。
ということを考えなければならないだろう。
とりあえず、この問題は一旦置いておいて、いったいどれくらいの母数があるのかを探ってみる。
for (a=1; a<178; a++) {
for (b=1; a+b<179; b++) {
for (c=1; a+b+c<180; c++) {
for (d=1; b+c+d<180; d++) {
/* ここに処理を書く */
}
}
}
}
for (b=1; a+b<179; b++) {
for (c=1; a+b+c<180; c++) {
for (d=1; b+c+d<180; d++) {
/* ここに処理を書く */
}
}
}
}
もしくは、
for (a=1; a<178; a++) for (b=1; a+b<179; b++) for (c=1; a+b+c<180; c++) for (d=1; b+c+d<180; d++) {
/* ここに処理を書く */
}
/* ここに処理を書く */
}
といったネスト(入れ子)になる。
因みに、このループの回数を数えると、83653681回となります。
a、b、c、dは整数を入れても、かならずxが整数となるかは解らないので、このループ回数が、整角四角形問題の総数ではないことは、明らかである。
そこで、以下のようなJavascriptのプログラムを考えてみた。
簡単に説明すると、
arctanで求めた小数のままのθ(プログラムではx)の値と、
整数化したθ(プログラムではy)の値との差の絶対値を、
log10を取ったもので分布してカウントする。
α=δ、すなわち円に内接する、
α≠δ、すなわち円に内接しない、
も同様に別途カウントする。
var a, b, c, d, e, f, g, h, x, y;
var deg=180.0/Math.PI;
var rad=Math.PI/180.0;
var count0=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var count1=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var count2=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
for (a=1; a<178; a++) for (b=1; a+b<179; b++) for (c=1; a+b+c<180; c++) for (d=1; b+c+d<180; d++) {
e = Math.tan(c*rad)/(Math.tan((a+b)*rad)+Math.tan(c*rad));
f = Math.tan(b*rad)/(Math.tan((d+c)*rad)+Math.tan(b*rad));
g = e*Math.tan((a+b)*rad);h = f*Math.tan((d+c)*rad);
x = b-Math.atan2(h-g,1-e-f)*deg;
switch ( parseInt(x*10)%10 ) {
case 0:
y = parseInt(parseInt(x*10)/10);
break;
case 9:
y = parseInt(parseInt(x*10+1)/10);
break;
default:
y = -1;
break;
}
if ( y > 0 ) {
if ( Math.abs(x-y) > 0.0 ) {
count0[-parseInt(Math.log10(Math.abs(x-y)))]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
} else {
count0[count0.length-1]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
}
} else {
count0[0]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
}
}
var str = '<table><tr align="center"><td>10^-n</td><td style="width: 15ch;">全事象</td><td style="width: 15ch;">円に内接する</td><td style="width: 15ch;">円に内接しない</td></tr>';
for (var i=0; i<count0.length; i++) str += '<tr align="right"><td>'+i+'</td><td>'+count0[i]+'</td><td>'+count1[i]+'</td><td>'+count2[i]+'</td></tr>';
str += '</table>';
document.getElementById('inner').innerHTML = str;
var deg=180.0/Math.PI;
var rad=Math.PI/180.0;
var count0=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var count1=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var count2=new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
for (a=1; a<178; a++) for (b=1; a+b<179; b++) for (c=1; a+b+c<180; c++) for (d=1; b+c+d<180; d++) {
e = Math.tan(c*rad)/(Math.tan((a+b)*rad)+Math.tan(c*rad));
f = Math.tan(b*rad)/(Math.tan((d+c)*rad)+Math.tan(b*rad));
g = e*Math.tan((a+b)*rad);h = f*Math.tan((d+c)*rad);
x = b-Math.atan2(h-g,1-e-f)*deg;
switch ( parseInt(x*10)%10 ) {
case 0:
y = parseInt(parseInt(x*10)/10);
break;
case 9:
y = parseInt(parseInt(x*10+1)/10);
break;
default:
y = -1;
break;
}
if ( y > 0 ) {
if ( Math.abs(x-y) > 0.0 ) {
count0[-parseInt(Math.log10(Math.abs(x-y)))]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
} else {
count0[count0.length-1]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
}
} else {
count0[0]++;
if ( a == d ) {
count1[-parseInt(Math.log10(Math.abs(x-y)))]++;
} else {
count2[-parseInt(Math.log10(Math.abs(x-y)))]++;
}
}
}
var str = '<table><tr align="center"><td>10^-n</td><td style="width: 15ch;">全事象</td><td style="width: 15ch;">円に内接する</td><td style="width: 15ch;">円に内接しない</td></tr>';
for (var i=0; i<count0.length; i++) str += '<tr align="right"><td>'+i+'</td><td>'+count0[i]+'</td><td>'+count1[i]+'</td><td>'+count2[i]+'</td></tr>';
str += '</table>';
document.getElementById('inner').innerHTML = str;
出力結果は、このようになった。
10^-n | 全事象 | 円に内接する | 円に内接しない |
0 | 66750978 | 0 | 66750978 |
1 | 14411215 | 0 | 14411215 |
2 | 1353328 | 0 | 1353328 |
3 | 123471 | 0 | 123471 |
4 | 11412 | 0 | 11412 |
5 | 496 | 0 | 496 |
6 | 48 | 0 | 48 |
7 | 0 | 0 | 0 |
8 | 0 | 0 | 0 |
9 | 0 | 0 | 0 |
10 | 314 | 308 | 6 |
11 | 6746 | 6594 | 152 |
12 | 75134 | 71925 | 3209 |
13 | 433139 | 407665 | 25474 |
14 | 213083 | 197280 | 15803 |
15 | 5756 | 5604 | 152 |
16 | 0 | 0 | 0 |
17 | 0 | 0 | 0 |
18 | 0 | 0 | 0 |
19 | 268561 | 250553 | 18008 |
まず、円に内接するケースに着目すると、円に内接するすなわち、α=δより、円周角よりθ=γとなり、整角四角形問題となることは明白である。
故に、円に内接するの分布域は、すべて誤差の範疇と言える。
円に内接しないケースも同様な分布域、もしくは多少精度の悪い分布域を示す可能性はあったが、10^-nの7~9に明らかなフタコブラクダの谷間が出来ており、同様に8以降は誤差の範疇と考えるに至る。
これより、
整角四角形問題の総数は、
314+6746+75134+433139+213083+5756+268561=1002733
円に内接するものは、
308+6594+71925+407665+197280+5604+250553=939929
円に内接しないものは、
6+152+3209+25474+15803+152+18008=62804
という個数が求まったのである。
同様のことを、C言語でやってみたが、結果は同じようにフタコブラクダの谷間ができ、誤差と捉えた部分の分布は違っても、上記の合計は全て等しかった。
ただ、プログラミング言語においても、フタコブラクダの谷間が0となるかは、関数や変数の精度次第だろう。
さて、プログラミングはここまでとし、数学にもどる。
ラングレーの問題、フランクリンの凧、整角四角形問題、
いずれにしても、初等的な逆三角関数なら使ったとしても、解析的な逆三角関数を使わずに解くということが、この問題の醍醐味である。
どれだけ単純で簡単な方法で導くかというところの勝負でもある。
- ラングレーの問題にトドメをさす!―4点の作る小宇宙完全ガイド/現代数学社
- ¥価格不明
- Amazon.co.jp
- 現代数学 2016年 02 月号 [雑誌]/現代数学社
- ¥価格不明
- Amazon.co.jp
本当に1002733個すべてを分類して、すべてにおいて初等幾何学、代数学の範疇で答えを導き出せたのだろうか。
これらの書籍を読めば解るのだろうか。
まぁ、1002733という総数とラングレーの問題等とを紐付けしてネットで検索しても、みつかるのは、こののブログくらいなものだろう。
さて、Owndにでもプログラムを書こうかな。
ではでは