鶏ドリアンさんの情報の講義のC言語の課題その3
- カテゴリ:パソコン/インターネット
- 2010/08/03 02:45:49
これは、ほぼ同じタイトル(その2)のブログの続編です。
前回は
http://www.nicotto.jp/blog/detail?user_id=288939&aid=16781656です。
初回は、
http://www.nicotto.jp/blog/detail?user_id=288939&aid=16691800
です。
まずテーマの確認。
課題は以下の通り。
自然数nを入力させる。
そのnに対して、「1」、「2」、…「n-1」、「n」の番号が書かれたn枚のカードを仮定する。
このn枚のカードを「n」が最上面になるように番号の順に積む。
その後、「最上面のカードを最下面に移動し、その次に最上面になったカードを捨てる」
という操作を繰り返し、最後にのこったカードの番号を出力せよ。
というわけです。
さて、ここで、枚数を2で割りながら、偶数枚の時にはそのまま、奇数枚の時には、一番上のカードを取り除く、その下のカードは、枚数を2で割った回数分2のべき乗、という感じの手続きまでたどりつきました(詳しくはその2を参照)
さて、これは、イメージとして、Nを2進表記して、下の方からビットを見ていき、0なら何もせず、1なら、その桁位置*2の数を引く、という操作をしていることになります。ただし最上位ビットには何もしない。
これをビット操作で見ると、
(1)最上位ビットを取り除く。
(2)左シフト(2倍する)
(3)その結果を元の数から引く。
という一連の操作で解が求まることが分かります。
こういう作業は、マシン語の方が簡単で、ビットを調べる操作は要するに2で割っていく操作ですから、前回のルーチンと大同小異ということになります。
今回は、上の(1)~(3)の操作を、2進変換せずに強引に関数計算で計算しちゃおうというわけです。
(1)最上位ビットを取り除く。
最上位ビットの桁を求めるには、要するに2進の桁数を見ればいいので、
INT(LOG2(N)) です。
最上位ビットだけの数は、2^INT(LOG2(N))となります。(LOG2(x)は2を底としたxの対数を与える「なでしこ」の組み込み関数です)
これをNから取り除くのですから、N-2^INT(LOG2(N)) が該当する計算式になります。
(2)2倍する。
{(N-2^INT(LOG2(N))}*2
(3)元の数から引く
N-{N-2^INT(LOG2(N))}*2=2^INT(LOG2(N))*2-N
ということで、プログラムはこちら。
##################################################
# トリさんの課題、別解2
# 2010/08/03
# 自然数 N の入力
「自然数 N を入力してください」で尋ねてNに代入する。
# 入力チェック
もし(Nの整数部分<>N)OR (N<=0)ならば
「入力するのは自然数だけにしてね(^_-)-☆」と言う。
終わる。
# 先頭カード番号=N-(N-2^INT(LOG2(N)))*2=2^INT(LOG2(N))*2-N
先頭カード番号=2^INT(LOG2(N))*2-N
先頭カード番号を言う。
終わる。
# main routine 終了
##################################################
処理は実質1行になってしまいました。
ただ関数を使っているので、正しく計算できる数の上限はちと気になります。実務プログラミングでは、ちゃんと計算可能な範囲をチェックするか、あるいは不確実な実数演算を使うよりは、一つ前のルーチンを使う方が安全確実バグ防止になるかもしれないです。
しかし、こうなると、「仕様」とプログラム(この1行の式)との関連性は、一見しただけでは全く分からないですね~。他人どころか、自分でも1ヵ月もしたら、なんでこの式になるんだっけ(;゚-゚)??となってしまいそうです。もちろん、その2で述べたように、仕様変更に対して極めて脆弱、お手上げです。
今回は、最初のプログラム(単純にコーディング)で済むはずでしたが、鶏ドリアンさんの解法を見て、思わずのめり込んでしまいました~~。久しぶりにプログラミング領域で楽しかったです。いや、こんな事するなら、朝みんクロ研究室で使うプログラムの開発を先に進めろってね(^_^;)
# トリさんの課題、別解3
# 2010/08/03
# 自然数 N の入力
「自然数 N を入力してください」で尋ねてNに代入する。
# 入力チェック
もし(Nの整数部分<>N)OR (N<=0)ならば
「入力するのは自然数だけにしてね(^_-)-☆」と言う。
終わる。
# N を2進数で考えた時の、最上位ビットを捨て、2倍する。
# Nからそれを引く。
# というのが、解になるのだが、Nを2進数で考えたときの最上位ビットだけが立った数を
# 「最上位ビット桁位置数」という名前を付けると、
# 答えは N-(N-最上位ビット桁位置数)*2
# = 最上位ビット桁位置数*2 - N
# となる。
# 最上位ビット桁位置数とは、2のべき乗数で、Nを超えない最大のものである。
# 最上位ビット桁位置数*2とは、2のべき乗数で、Nより大きな物の内の最小のものである。
# べき乗関数も対数関数も使わないという条件でコーディングする。
# 最上位ビット桁位置数*2を「最上位ビット桁位置数乃弐倍」という変数名を付けておく
最上位ビット桁位置数乃弐倍=1
N>=最上位ビット桁位置数乃弐倍の間
最上位ビット桁位置数乃弐倍に2を直接掛ける
先頭カード番号=最上位ビット桁位置数乃弐倍 - N
先頭カード番号を言う。
終わる。
# main routine 終了
単に2を掛け続けて、Nを超えた時の値が最上位ビット桁位置数乃弐倍なので、これからNを引くだけですね~(^^)
なぜかわからないんですが、math.h読み込んでもpow関数が使えないんですよ。
(未定義がど~たらこ~たらという趣旨と思わしき英文がダラダラと)
しょうがないので再定義するはめになりまして
そうなると、log2関数も再定義せなアカンわけですよ。
そうなると…逆に全文が長くなりそうなんですよね。
なので、今の形に落ち着いたんです