2015年10月25日
エラー処理
趣味であれ仕事であれ、プログラム作成で一番頭を悩ませるのはエラー処理だと思っています。
何か新しいアルゴリズムを考案したり理解したりしてプログラムを作成するというのは、少なくとも日曜プログラミングなんかをやっている方なら非常に楽しい作業なのではないかと思います。しかし、不測の事態が起きた場合や、意図しない入力があった場合に備えてきちんとしたエラー処理を組み込もうとした途端に楽しかったはずの作業が苦痛に変わります。個人的には、デバッグ同様できるだけ避けたい作業の一つで、退屈なだけにエラー処理を考える方が苦痛に感じます。
エラー処理は、プログラムも冗長にしがちです。それを避けるために「例外処理」という機構が C++ や Java、C# などでは用意されています。例外を上手に使えば、プログラムを非常にシンプルにすることができます。しかし、C 言語などで組んでいるために既存のプログラムが例外を全く使っていない場合、そのプログラムは全て見直しが必要になるので、google のスタイルガイドでは「C++ で例外は使ってはいけない」としています。
例外処理を利用しない場合、外部から利用するための関数を作成した時に外部にエラーを通知するためには
1) エラーコードを戻り値として渡す
2) エラーメッセージを画面に出力する
3) 何もせずに戻る
くらいの方法が考えられます。
戻り値にエラーコードを返す場合、戻り値は通常、他の用途に使えなくなるので、何らかの値を得たい場合は引数を通して得る必要があります。また、エラーコード自体もきちんと管理しなければなりません。それから、エラーメッセージを出力しても、それがプログラム側に伝わるわけではなく、これだけで充分ということはまずないでしょう。何もしないというのは、外部にエラーが発生したことが検知できないことを意味しますが、結果からそれを知ることができれば充分ということも場合によってはあります。たいていは、エラーコードとエラーメッセージの組み合わせを利用しますが、それを呼び出し元がきちんと見てくれるかという問題もあり、関数が出力するエラー全てに対して一つ一つきちんと対応するコードを書いていたらキリがなく、どこかで妥協することが多くなります。
例外処理を利用すると、例外は呼び出し元に次々と伝播していきます。通常のアプリなら、メイン・ルーチンのところだけで例外を捕捉して、メッセージを出力して終了としておくだけでも十分な場合が多く、それなら途中の関数などで例外の補足時に行わなければならないことを必要最低限な部分だけ書いておけば、エラー処理部分はかなり簡潔にまとめることができます。
ということで、特に Java や C# であれば、エラー処理は基本的に例外を使うことになるし、C++ だけでコーディングするのなら迷わず例外を使うべきでしょうね。
何か新しいアルゴリズムを考案したり理解したりしてプログラムを作成するというのは、少なくとも日曜プログラミングなんかをやっている方なら非常に楽しい作業なのではないかと思います。しかし、不測の事態が起きた場合や、意図しない入力があった場合に備えてきちんとしたエラー処理を組み込もうとした途端に楽しかったはずの作業が苦痛に変わります。個人的には、デバッグ同様できるだけ避けたい作業の一つで、退屈なだけにエラー処理を考える方が苦痛に感じます。
エラー処理は、プログラムも冗長にしがちです。それを避けるために「例外処理」という機構が C++ や Java、C# などでは用意されています。例外を上手に使えば、プログラムを非常にシンプルにすることができます。しかし、C 言語などで組んでいるために既存のプログラムが例外を全く使っていない場合、そのプログラムは全て見直しが必要になるので、google のスタイルガイドでは「C++ で例外は使ってはいけない」としています。
例外処理を利用しない場合、外部から利用するための関数を作成した時に外部にエラーを通知するためには
1) エラーコードを戻り値として渡す
2) エラーメッセージを画面に出力する
3) 何もせずに戻る
くらいの方法が考えられます。
戻り値にエラーコードを返す場合、戻り値は通常、他の用途に使えなくなるので、何らかの値を得たい場合は引数を通して得る必要があります。また、エラーコード自体もきちんと管理しなければなりません。それから、エラーメッセージを出力しても、それがプログラム側に伝わるわけではなく、これだけで充分ということはまずないでしょう。何もしないというのは、外部にエラーが発生したことが検知できないことを意味しますが、結果からそれを知ることができれば充分ということも場合によってはあります。たいていは、エラーコードとエラーメッセージの組み合わせを利用しますが、それを呼び出し元がきちんと見てくれるかという問題もあり、関数が出力するエラー全てに対して一つ一つきちんと対応するコードを書いていたらキリがなく、どこかで妥協することが多くなります。
例外処理を利用すると、例外は呼び出し元に次々と伝播していきます。通常のアプリなら、メイン・ルーチンのところだけで例外を捕捉して、メッセージを出力して終了としておくだけでも十分な場合が多く、それなら途中の関数などで例外の補足時に行わなければならないことを必要最低限な部分だけ書いておけば、エラー処理部分はかなり簡潔にまとめることができます。
ということで、特に Java や C# であれば、エラー処理は基本的に例外を使うことになるし、C++ だけでコーディングするのなら迷わず例外を使うべきでしょうね。
2015年10月21日
千日回峰行
比叡山延暦寺に伝わる荒行で「千日回峰行」というものがあります。
以前、「千日回峰行」を行った僧侶がテレビに出演していて、その荒行ぶりを語っていたのを見たことがあります。1000 日間、往復 48km の山道を一日も欠かさず歩くというかなり厳しいものです。しかも、途中で止めることは許されず、その場合は自ら命を絶たなければなりません。そのための短刀と紐を常に携帯して修行するそうで、何とも厳しい掟です。この方、海外でも講話をされていて「マラソン・モンク」と呼ばれているそうです。一番すさまじいと思ったのが、
朝、目が覚めても体が動かない。
何とか這って山頂を目指そうとしたが途中で倒れた。
「石にかじりついてでも」という母の言葉を思い出し、本当に石をかじって立ち上がった。
山頂にたどり着いたとき、体中から湯気がもうもうと立ち込めていた。
と語っていたところ(このあたり、少しうろ覚えです)。まさに「死を覚悟して」の荒行ですね。
今日の夕刊で、ある僧侶が、この修業の中でも難行と言われる「堂入り」を終えたとの記事を見ました。九日間、断食・断水・不眠・不臥 ( 四無行というそうです ) を行うというものですが、これが終わった後もあと 300 日間、修行は続くのだそうです。まさに超人ですね。
以前、「千日回峰行」を行った僧侶がテレビに出演していて、その荒行ぶりを語っていたのを見たことがあります。1000 日間、往復 48km の山道を一日も欠かさず歩くというかなり厳しいものです。しかも、途中で止めることは許されず、その場合は自ら命を絶たなければなりません。そのための短刀と紐を常に携帯して修行するそうで、何とも厳しい掟です。この方、海外でも講話をされていて「マラソン・モンク」と呼ばれているそうです。一番すさまじいと思ったのが、
朝、目が覚めても体が動かない。
何とか這って山頂を目指そうとしたが途中で倒れた。
「石にかじりついてでも」という母の言葉を思い出し、本当に石をかじって立ち上がった。
山頂にたどり着いたとき、体中から湯気がもうもうと立ち込めていた。
と語っていたところ(このあたり、少しうろ覚えです)。まさに「死を覚悟して」の荒行ですね。
今日の夕刊で、ある僧侶が、この修業の中でも難行と言われる「堂入り」を終えたとの記事を見ました。九日間、断食・断水・不眠・不臥 ( 四無行というそうです ) を行うというものですが、これが終わった後もあと 300 日間、修行は続くのだそうです。まさに超人ですね。
2015年10月18日
Tomb Raider 再び
Tomb Raider の続編が来月に発売されるそうです。しかし、今のところ XBOX のみのようですね。
Play Station で発売されたとしてもおそらく PS4 だけになるんじゃないでしょうか。PC 版が出たら買おうかと思います。当然、安くなってから。
昔からゲーム自体は好きなんですけど、最近は途中でやめてしまうことが多くなってきました。なので、ほとんど買わなくなって、PS3 も録画したテレビを見るくらいしか使っていません。ゲームするために起動したのはもう何ヶ月も前のこと。ゲームの紹介画面とかを見るとプレイしたくなるものはいろいろあるんですが、いざ買ってみると、かなり気力がないと続けられなくなりました。熱中するほどおもしろいと感じなくなってきたのもあるのかも。
そんな中で、Tomb Raider は結構楽しめたゲームでした。これは続編もプレイしてみたいです。しかし、PS4 はもう買うことはないかな。
Play Station で発売されたとしてもおそらく PS4 だけになるんじゃないでしょうか。PC 版が出たら買おうかと思います。当然、安くなってから。
昔からゲーム自体は好きなんですけど、最近は途中でやめてしまうことが多くなってきました。なので、ほとんど買わなくなって、PS3 も録画したテレビを見るくらいしか使っていません。ゲームするために起動したのはもう何ヶ月も前のこと。ゲームの紹介画面とかを見るとプレイしたくなるものはいろいろあるんですが、いざ買ってみると、かなり気力がないと続けられなくなりました。熱中するほどおもしろいと感じなくなってきたのもあるのかも。
そんな中で、Tomb Raider は結構楽しめたゲームでした。これは続編もプレイしてみたいです。しかし、PS4 はもう買うことはないかな。
2015年10月15日
脱ヌード
Playboy Magazine が、ヌードの掲載をやめるという記事が新聞に載っていました。Live Science でもこの話題を取り上げて、"Bye, Bye, Playboy, Bunnies" という記事を掲載していました。副題が「ポルノが脳に与える五つの影響」ということで、果たしてポルノは脳にいい影響を与えるのかどうか、全く正反対な二つの作用について書かれています。
今ではインターネットの普及で Web 上でも簡単にヌード画像が閲覧できてしまうので、ヌード写真を掲載した雑誌というものも淘汰されてきているようです。プレイボーイも同様で、売り上げは下降状態。で、ヌードの掲載を一時的にやめたら逆に売り上げが伸びたそうです。年齢層も下がったということで、ヌードがなくなって買いやすくなったんでしょうか。いやいや、中学生じゃあるまいし。
「数学問題bot」から、なかなか面白い問題です。
-----
n! が n^2 の倍数となるような自然数 n を全て求めよ ( 11 東工大 AO )
n! = Kn2 より、両辺を n で割って
( n - 1 )! = Kn となるような自然数 K が存在すればいいので、( n - 1 )! が n で割り切れるような自然数をすべて求めればよいことになります。n = 1 ならば明らかに成り立ち、n = 2 は成り立たないことは簡単に示すことができます。n = 3 の場合、( n - 1 )! = 2 は 3 で割り切れないので成り立ちません。n = 4 では ( n - 1 )! = 6 となり、これもNGです。以下、計算すると
となります。まず、素数に対しては成り立たないようにみえるのでこれを証明すると、p を素数とした時、1 から p - 1 までのすべての数は p と互いに素です。従って、( p - 1 )! には p と共通な因数は存在せず、従って成り立たないことがわかります。しかし、p2 ならば、1 から p2 までの間の p の倍数は p, 2p, ... p2 の p 個あるので、( p2 - 1 )! には p が p - 1 個含まれることになり、p - 1 ≥ 2 ならば、( p2 - 1 )! は p2 で割り切れます。従って、p ≥ 3 ならば、p2 は成り立つことになります。
さらに一般化して pm ( m ≥ 2 ) の場合を考えると、1 から pm までの間の p の倍数は p, 2p, ... pm-1・p の pm-1 個あり、k ≤ m の任意の k について、pk の倍数は pk, 2pk, ... pm-kpk の pm-k 個あります。pk の倍数には k 個の p があり、k より小さな指数の倍数でもあることから、k = 1 の場合から順に p をひとつずつ抽出したと考えれば、p の総数は
pm-1 + pm-2 + ... + p + 1 = pm - 1 個
になります。しかし、これは pm! にある p の総数なので、実際にはここから m を引いた個数が ( pm - 1 )! に含まれる p の個数です。従って
pm - m - 1 ≥ m より
pm ≥ 2m + 1
ならば pm は成り立つことになります。この不等式は、( p, m ) = ( 2, 2 ) 以外の全ての数 ( 但し m ≥ 2 です ) で成り立ちます。
最後に、n を二つ以上の異なる素因数からなる任意の合成数としたとき、その中の一つの素因数を p として n は
n = Kpm
という形で表すことができます。但し、K ≥ 2, m ≥ 1 とします。( n - 1 )! には pm の倍数が K - 1 個あるので、K ≥ 2 ならば必ず pm で割り切れることになります。どの素因数に対してもこれは成り立つので、二つ以上の異なる素因数を持つ合成数は全て成り立つことになります。
従って、求める自然数は、
1
22 を除いた素数のべき乗数全て
二つ以上の異なる素因数からなる合成数
になります。
例によって、合ってるかどうかは不明です。
今ではインターネットの普及で Web 上でも簡単にヌード画像が閲覧できてしまうので、ヌード写真を掲載した雑誌というものも淘汰されてきているようです。プレイボーイも同様で、売り上げは下降状態。で、ヌードの掲載を一時的にやめたら逆に売り上げが伸びたそうです。年齢層も下がったということで、ヌードがなくなって買いやすくなったんでしょうか。いやいや、中学生じゃあるまいし。
「数学問題bot」から、なかなか面白い問題です。
-----
n! が n^2 の倍数となるような自然数 n を全て求めよ ( 11 東工大 AO )
n! = Kn2 より、両辺を n で割って
( n - 1 )! = Kn となるような自然数 K が存在すればいいので、( n - 1 )! が n で割り切れるような自然数をすべて求めればよいことになります。n = 1 ならば明らかに成り立ち、n = 2 は成り立たないことは簡単に示すことができます。n = 3 の場合、( n - 1 )! = 2 は 3 で割り切れないので成り立ちません。n = 4 では ( n - 1 )! = 6 となり、これもNGです。以下、計算すると
n | ( n - 1 )! | 割り切れるか? |
5 | 24 | × |
6 | 120 | ○ |
7 | 720 | × |
8 | 5040 | ○ |
9 | 40320 | ○ |
10 | 362880 | ○ |
となります。まず、素数に対しては成り立たないようにみえるのでこれを証明すると、p を素数とした時、1 から p - 1 までのすべての数は p と互いに素です。従って、( p - 1 )! には p と共通な因数は存在せず、従って成り立たないことがわかります。しかし、p2 ならば、1 から p2 までの間の p の倍数は p, 2p, ... p2 の p 個あるので、( p2 - 1 )! には p が p - 1 個含まれることになり、p - 1 ≥ 2 ならば、( p2 - 1 )! は p2 で割り切れます。従って、p ≥ 3 ならば、p2 は成り立つことになります。
さらに一般化して pm ( m ≥ 2 ) の場合を考えると、1 から pm までの間の p の倍数は p, 2p, ... pm-1・p の pm-1 個あり、k ≤ m の任意の k について、pk の倍数は pk, 2pk, ... pm-kpk の pm-k 個あります。pk の倍数には k 個の p があり、k より小さな指数の倍数でもあることから、k = 1 の場合から順に p をひとつずつ抽出したと考えれば、p の総数は
pm-1 + pm-2 + ... + p + 1 = pm - 1 個
になります。しかし、これは pm! にある p の総数なので、実際にはここから m を引いた個数が ( pm - 1 )! に含まれる p の個数です。従って
pm - m - 1 ≥ m より
pm ≥ 2m + 1
ならば pm は成り立つことになります。この不等式は、( p, m ) = ( 2, 2 ) 以外の全ての数 ( 但し m ≥ 2 です ) で成り立ちます。
最後に、n を二つ以上の異なる素因数からなる任意の合成数としたとき、その中の一つの素因数を p として n は
n = Kpm
という形で表すことができます。但し、K ≥ 2, m ≥ 1 とします。( n - 1 )! には pm の倍数が K - 1 個あるので、K ≥ 2 ならば必ず pm で割り切れることになります。どの素因数に対してもこれは成り立つので、二つ以上の異なる素因数を持つ合成数は全て成り立つことになります。
従って、求める自然数は、
1
22 を除いた素数のべき乗数全て
二つ以上の異なる素因数からなる合成数
になります。
例によって、合ってるかどうかは不明です。
2015年10月11日
ポインタの呪い
明日は体育の日で休みですね。
C言語を使う上で避けることのできない(使わなくてもなんとかなるかもしれませんが)概念に「ポインタ」があります。
int i = 3;
int* ip = &i;
と書くと、ip は i へのポインタ ( i を指し示すもの ) となって、
int j = *ip;
と書けば j に ip が指し示す i の値が代入され、
*ip = 6;
と書けば ip が指し示す i に 6 が代入されます。
理解すれば非常に便利なポインタですが、これのせいで C 言語のマスターをあきらめたという人も多いようです。たいていは、変数がメモリ上に保持される様子を使って説明されていたりして、特に初心者にとってはわかりづらいのではないのでしょうか。また、文法そのものもとっつきにくい原因となっていると思います。関数からポインタそのものを受け取りたいような場合は
void f( size_t sz, char** cpp )
{
*cpp = malloc( sz );
}
という具合に '**' で「ポインタのポインタ(ハンドル)」を表します。さらに関数ポインタの配列などは型の書き方がややこしくてたいていは忘れてしまい、本を見て思い出すといった具合です。C++ では参照が使えるようになって、ポインタはあまり意識しなくてもいいようになりました。関数ポインタも、関数オブジェクトというさらに便利な機能によってほとんど利用せずに済みます。
さらに Java や C# は「オブジェクトは参照渡しで組み込み変数は値渡し」と決められているので、完全にポインタのようなややこしい部分は意識しなくてよくなったわけですが、「参照渡しだから」ということで関数内で新しいインスタンスを構築して渡そうとしてうまくいかないといった失敗例をよく見かけます。
void f( SomeClass sc )
{
sc = new sc( ... );
}
この場合、C 言語でいうところの "普通の" ポインタ渡しを意味するので、オブジェクトのある位置が変数として渡されるだけです。その変数に新たなインスタンスの位置を代入しても、元のインスタンスは影響を受けないので意味がないわけです。ちなみに C# の場合はハンドルを渡すために ref キーワードがありますね。
void f( ref SomeClass sc )
{
sc = new sc( ... );
}
とすれば、意図したとおり新たなインスタンスで書き換えてくれます。
もし、このあたりで悩んでいる方がいれば、参考になれば幸いです。
C言語を使う上で避けることのできない(使わなくてもなんとかなるかもしれませんが)概念に「ポインタ」があります。
int i = 3;
int* ip = &i;
と書くと、ip は i へのポインタ ( i を指し示すもの ) となって、
int j = *ip;
と書けば j に ip が指し示す i の値が代入され、
*ip = 6;
と書けば ip が指し示す i に 6 が代入されます。
理解すれば非常に便利なポインタですが、これのせいで C 言語のマスターをあきらめたという人も多いようです。たいていは、変数がメモリ上に保持される様子を使って説明されていたりして、特に初心者にとってはわかりづらいのではないのでしょうか。また、文法そのものもとっつきにくい原因となっていると思います。関数からポインタそのものを受け取りたいような場合は
void f( size_t sz, char** cpp )
{
*cpp = malloc( sz );
}
という具合に '**' で「ポインタのポインタ(ハンドル)」を表します。さらに関数ポインタの配列などは型の書き方がややこしくてたいていは忘れてしまい、本を見て思い出すといった具合です。C++ では参照が使えるようになって、ポインタはあまり意識しなくてもいいようになりました。関数ポインタも、関数オブジェクトというさらに便利な機能によってほとんど利用せずに済みます。
さらに Java や C# は「オブジェクトは参照渡しで組み込み変数は値渡し」と決められているので、完全にポインタのようなややこしい部分は意識しなくてよくなったわけですが、「参照渡しだから」ということで関数内で新しいインスタンスを構築して渡そうとしてうまくいかないといった失敗例をよく見かけます。
void f( SomeClass sc )
{
sc = new sc( ... );
}
この場合、C 言語でいうところの "普通の" ポインタ渡しを意味するので、オブジェクトのある位置が変数として渡されるだけです。その変数に新たなインスタンスの位置を代入しても、元のインスタンスは影響を受けないので意味がないわけです。ちなみに C# の場合はハンドルを渡すために ref キーワードがありますね。
void f( ref SomeClass sc )
{
sc = new sc( ... );
}
とすれば、意図したとおり新たなインスタンスで書き換えてくれます。
もし、このあたりで悩んでいる方がいれば、参考になれば幸いです。
2015年10月07日
新しい窓達
仕事で Windows2012 の再インストール作業をすることになりました。初めて使った時は、あまりにも操作方法が変わってしまって設定にかなり手こずってましたが、最近はだいぶ慣れてきたような気が。しかし、リモートデスクトップサービスを使うのにドメインを立ちあげなければならなくなったりして結構面倒です。そういえば、次のバージョンはいつ頃出るんでしたっけね。クライアント用には Windows10 が発表されて間もないわけですが、今のところ使う気にはならないです。しかし、また PC を買い換える頃にはこれしかないような状態になりそうです。
今回は、「マテマティカ2」からの問題を解いてみました。ちょっと前からいろいろと解き方を考えていましたが、ようやく解法が見つかりました (正解しているかはわかりませんが)。
-----
153: 複素数
p を素数、a, b を互いに素である自然数とする。( a + bi )^p が実数をとるような a, b, p の組み合わせは何組あるか。ただし、i は虚数単位とする。
二項定理より
( a + bi )p = ap + pC1ap-1・bi + ... + pCkap-k・(bi)k + ... + pCp(bi)p
なので、虚部は bi の指数が奇数の部分で表されます。p = 2 のときは
( a + bi )2 = ( a2 - b2 ) + 2abi
なので、これが実数となるためには ab = 0 である必要がありこれは成り立ちません。従って、p は奇素数の場合だけを考えればよいことになります。任意の奇素数に対し、その虚部は
[ pC1ap-1・b - pC3ap-3・b3 + ... + (-1)(k-1)/2pCkap-k・bk + ... + (-1)(p-1)/2pCpbp ]i
= i・Σk{1→(p+1)/2}( (-1)k-1pC2k-1ap-2k+1・b2k-1 )
となります。従って、これがゼロになれば、( a + bi )p は実数となります。虚数 i を除いて係数部分だけとし、b ≠ 0 より、b で割ると
pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-1)/2pCpbp-1
= pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-1)/2bp-1
= 0
より
pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-3)/2pCp-2a2・bp-3 = -(-1)(p-1)/2bp-1
となりますが、p > k > 0 ならば pCk は必ず p の倍数となるので(証明は後述)、左辺は p の倍数となり、従って右辺にある b は p を素因数に持つ必要があります (左辺がゼロの場合 b = 0 となるのでこの場合は無視できます)。そこで今度は b = pc とすると、
pC1ap-1 - pC3ap-3・(pc)2 + ... + (-1)(k-1)/2pCkap-k・(pc)k-1 + ... + (-1)(p-1)/2(pc)p-1
= pC1ap-1 - p2pC3ap-3・c2 + ... + (-1)(k-1)/2pk-1pCkap-k・ck-1 + ...
+ (-1)(p-1)/2pp-1cp-1
= 0
より
-p2pC3ap-3・c2 + ... + (-1)(k-1)/2pk-1pCkap-k・ck-1 + ... + (-1)(p-1)/2pp-1cp-1 = -pC1ap-1
となり、両辺を p で割れば
-ppC3ap-3・c2 + ... + (-1)(k-1)/2pk-2pCkap-k・ck-1 + ... + (-1)(p-1)/2pp-2cp-1 = -ap-1
なので a も p を素因数として持つことになり、a, b が互いに素であるという仮定に反します。従って、( a + bi )p が実数となるような ( a, b, p ) の組み合わせは存在しないということになります。
p > k > 0 ならば pCk は必ず p の倍数となることの証明ですが、
pCk = p! / k!・( p - k )! = p・( p - 1 )・...・( p - k + 1 ) / k・( k - 1 )・...・2・1
より p が素数で p > k ならば、分母の 1 から k までの数で p を割ることはできないので、pCk が整数なら p はそのまま素因数として残ることになり、従って pCk は p の倍数となります。pCk が整数であることの証明は(組み合わせを表すので当然ではありますが)、
p-1Ck + p-1Ck-1
= ( p - 1 )! / k!・( p - k - 1 )! + ( p - 1 )! / ( k - 1 )!・( p - k )!
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ][ 1 / k + 1 / ( p - k ) ]
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ]{ [ ( p - k ) + k ] / k( p - k ) ]
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ][ p / k( p - k ) ]
= p! / k!・( p - k )! = pCk
より、pCk は p-1Ck + p-1Ck-1 の和で表すことができます。従って、p-1Ck が任意の k について全て整数なら、pCk も整数であることになります。p = 1 のとき、1C0 = 1C1 = 1 なので、帰納法により pCk は整数であることが証明されます。もちろん、p が素数でなくてもこれは成り立ちます。
例によって合っている保証はありません。
今回は、「マテマティカ2」からの問題を解いてみました。ちょっと前からいろいろと解き方を考えていましたが、ようやく解法が見つかりました (正解しているかはわかりませんが)。
-----
153: 複素数
p を素数、a, b を互いに素である自然数とする。( a + bi )^p が実数をとるような a, b, p の組み合わせは何組あるか。ただし、i は虚数単位とする。
二項定理より
( a + bi )p = ap + pC1ap-1・bi + ... + pCkap-k・(bi)k + ... + pCp(bi)p
なので、虚部は bi の指数が奇数の部分で表されます。p = 2 のときは
( a + bi )2 = ( a2 - b2 ) + 2abi
なので、これが実数となるためには ab = 0 である必要がありこれは成り立ちません。従って、p は奇素数の場合だけを考えればよいことになります。任意の奇素数に対し、その虚部は
[ pC1ap-1・b - pC3ap-3・b3 + ... + (-1)(k-1)/2pCkap-k・bk + ... + (-1)(p-1)/2pCpbp ]i
= i・Σk{1→(p+1)/2}( (-1)k-1pC2k-1ap-2k+1・b2k-1 )
となります。従って、これがゼロになれば、( a + bi )p は実数となります。虚数 i を除いて係数部分だけとし、b ≠ 0 より、b で割ると
pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-1)/2pCpbp-1
= pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-1)/2bp-1
= 0
より
pC1ap-1 - pC3ap-3・b2 + ... + (-1)(k-1)/2pCkap-k・bk-1 + ... + (-1)(p-3)/2pCp-2a2・bp-3 = -(-1)(p-1)/2bp-1
となりますが、p > k > 0 ならば pCk は必ず p の倍数となるので(証明は後述)、左辺は p の倍数となり、従って右辺にある b は p を素因数に持つ必要があります (左辺がゼロの場合 b = 0 となるのでこの場合は無視できます)。そこで今度は b = pc とすると、
pC1ap-1 - pC3ap-3・(pc)2 + ... + (-1)(k-1)/2pCkap-k・(pc)k-1 + ... + (-1)(p-1)/2(pc)p-1
= pC1ap-1 - p2pC3ap-3・c2 + ... + (-1)(k-1)/2pk-1pCkap-k・ck-1 + ...
+ (-1)(p-1)/2pp-1cp-1
= 0
より
-p2pC3ap-3・c2 + ... + (-1)(k-1)/2pk-1pCkap-k・ck-1 + ... + (-1)(p-1)/2pp-1cp-1 = -pC1ap-1
となり、両辺を p で割れば
-ppC3ap-3・c2 + ... + (-1)(k-1)/2pk-2pCkap-k・ck-1 + ... + (-1)(p-1)/2pp-2cp-1 = -ap-1
なので a も p を素因数として持つことになり、a, b が互いに素であるという仮定に反します。従って、( a + bi )p が実数となるような ( a, b, p ) の組み合わせは存在しないということになります。
p > k > 0 ならば pCk は必ず p の倍数となることの証明ですが、
pCk = p! / k!・( p - k )! = p・( p - 1 )・...・( p - k + 1 ) / k・( k - 1 )・...・2・1
より p が素数で p > k ならば、分母の 1 から k までの数で p を割ることはできないので、pCk が整数なら p はそのまま素因数として残ることになり、従って pCk は p の倍数となります。pCk が整数であることの証明は(組み合わせを表すので当然ではありますが)、
p-1Ck + p-1Ck-1
= ( p - 1 )! / k!・( p - k - 1 )! + ( p - 1 )! / ( k - 1 )!・( p - k )!
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ][ 1 / k + 1 / ( p - k ) ]
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ]{ [ ( p - k ) + k ] / k( p - k ) ]
= [ ( p - 1 )! / ( k - 1 )!・( p - k - 1 )! ][ p / k( p - k ) ]
= p! / k!・( p - k )! = pCk
より、pCk は p-1Ck + p-1Ck-1 の和で表すことができます。従って、p-1Ck が任意の k について全て整数なら、pCk も整数であることになります。p = 1 のとき、1C0 = 1C1 = 1 なので、帰納法により pCk は整数であることが証明されます。もちろん、p が素数でなくてもこれは成り立ちます。
例によって合っている保証はありません。
2015年10月04日
久々の練習
休日中の名古屋は青空で気持ちいい天気でした。
昔、いっしょに演奏していたグループのメンバと久々に顔を合わせて練習することになり、昨日は一日出掛けていました。演奏していたのはフォルクローレというジャンルの音楽で、「コンドルは飛んで行く」なんかは有名ですね。
いろいろと記憶から飛んでしまった部分もあって、過去に作った譜面やら歌詞やらコードの覚え書きやらを確認していたのですが、これが結構なボリュームになっていて、我ながら、当時はパワーがありあまっていたんだなあと実感しました。一度整理しておきたいと思ったまま放置していたので、どこに何があるのかまったくわからず、当時のパンフレットやら違うジャンルの譜面も含まれてます。懐かしくなって、練習そっちのけでしばらく眺めていました。
ある程度メドがたてば復活ライブなんかもしたいですけど、いつの事になるかわからないです。昨日の練習だけで結構手が痛くなりました。
昔、いっしょに演奏していたグループのメンバと久々に顔を合わせて練習することになり、昨日は一日出掛けていました。演奏していたのはフォルクローレというジャンルの音楽で、「コンドルは飛んで行く」なんかは有名ですね。
いろいろと記憶から飛んでしまった部分もあって、過去に作った譜面やら歌詞やらコードの覚え書きやらを確認していたのですが、これが結構なボリュームになっていて、我ながら、当時はパワーがありあまっていたんだなあと実感しました。一度整理しておきたいと思ったまま放置していたので、どこに何があるのかまったくわからず、当時のパンフレットやら違うジャンルの譜面も含まれてます。懐かしくなって、練習そっちのけでしばらく眺めていました。
ある程度メドがたてば復活ライブなんかもしたいですけど、いつの事になるかわからないです。昨日の練習だけで結構手が痛くなりました。