石(掘る猫の)Blog

一割の情報、九割の水増し

目次

Blog 利用状況

ニュース

Twitter ID -> maincat

書庫

ぱぱっと書かせるプログラムの問題、の続き

皆さんいろいろと知恵を絞って頂けたようで、ありがとうございます。
この問題は私がやや天狗になっていた頃採用担当の方に出題して
頂いた問題で、回答例を頂いたときの感動は今でも昨日の事の様に
思い出せる程です。

 

与えられた自然数(0を含む)が2のべき乗数か否か判定し
そのbooleanを返すインターフェイス isAPo2 を作成せよ

 

バイナリイメージで捉える

2のべき乗数は「1, 2, 4, 8, 16, …」という数ですが、これをメモリ上で
表現すると次のようになります。
image

10進数において10のべき乗数が「1, 10, 100, 1000…」と繰り上がって
いくのと同様、2進数のメモリ上で表現される2のべき乗数とはご覧の
ようにある桁のみが1、他が全て0となります。この特徴を念頭に額に
汗して書き出した私のコードは、次のようなものでした。

bool isAPo2( int n ) {
	bool flag = false;
	while( 0 < n ) {
		if( n & 1 ) {
			if( flag ) { 
				return false;
			} else {
				flag = true;
			}
		}
		n >>= 1;
	}
	return flag;
}

値nが0以下のときループに入らずフラグの初期値falseを返します。
次にnが0より大の間、1の位を確認(行4)しながら右シフトをし続け、
1の回数が2回になればfalseを返し(行6)、最後まで抜ければtrue
がセット(行8)されたflagを返します(行13)。

 

同様の回答は、とっちゃんさんも示されています。場合によっては
ループ回数が少なくてすむ私のコードの方がすごいもんね><ネ。

int isAPo2(int num) 
{ 
 int onBit; 
 onBit = 0; 
 while( num > 0 ){ 
  if( num & 1 ){ 
   onBit++; 
  } 
  num >>= 1; 
 } 
 return onBit == 1; 
} 

 

算数の問題として捉える

これと同じ発想で回答をしていただいたのが、Hirotowさんです。

public isAPo2(int num) 
{ 
	while(num > 2 && num % 2 == 0){ 
		num /= 2; 
	} 
	return num == 2; 
} 

右シフトというのは、全てのビットを右に寄せる操作を指します。
桁を1つ下げることは2で割る(行4)ことと等しく、剰余が0(行3)と
いうことは1桁目のビットが0であることと等しくなります。ただし、
この判定方法だと1(=2^0)が除外されてしまうのが残念ですね。

 

マスクを動かす

ma2さんは原理は同じものの逆の発想で回答いただきました。

bool isAPo2(int num) 
{ 
	int mask = 1; 
	int cnt = 0; 
	for(int pos = 0;pos < (sizeof(int) * 8);pos++){ 
		if( num & mask ){ 
			cnt++; 
		} 
		mask <<= 1; 
	} 
	return (cnt == 1); 
} 

初期値1のマスクmaskを用意し、マッチングしながらマスクを
左シフト(行9)しています。これは「1, 2, 4, 8…」と2倍し続けて
いるということです。そして1の個数を数え(行7)、それが1個
であるか否かを返しています(行11)。これなら1もばっちり☆

 

出題者が提示された回答

ここまでは、言ってみれば王道。うんうん、そうだよねと頷く
もっともな回答です。答えあわせをするとき、パターンは色々
あれど原理は同じだろうと思っていると、次のような回答を
頂きました。

#define isAPo2( num ) ( num && !( num & num - 1 ) )

どうしてこれで題意が満たせるのか、お分かりでしょうか。
ぜひ考えてみてください。私は中々分かりませんでした。

 

よろしいでしょうか。それでは、解説に参りましょう。

image 1(2^0)の場合 !( 1 & 1 - 1 ) = true

image  4(2^2)の場合 !( 4 & 4 - 1 ) = true

image 20の場合 !( 20 & 20 - 1 ) = false

image 32(2^5)の場合 !( 32 & 32 - 1 ) = true

image 88の場合 !( 88 & 88 - 1 ) = false

 

こうして表にしてみると一目瞭然ですね。値から1を引き、さらに
その二つの論理積をとると、2のべき乗数なら0に、そうでないなら
0以外になります。従って、その否定が2のべき乗数か否かを指す
booleanなるのです。なお、0についてはnum &&ではじかれます。

与えられた自然数(0を含む)が2のべき乗数か否か判定し
そのbooleanを返すインターフェイス isAPo2 を作成せよ

この回答を理解したときの感動は、今でも思考の原動力となって
います。

 

と、いうわけで。

boolean isAPowerOf2( int num ) { 
	if (num <= 0) { return false; } 
	return !( num & (num-1) ); 
} 

全くそのままの回答をされたれいさんに惜しみない拍手をー。
くるかな、くるかなとは思っていましたが、本当にくるとはー。
わんくまは怖いところです。

 

力尽きてしまったので一行返信なのです

黒龍さん:解説エントリーはまだですかー><。

Ognacさん:解説エントリーはまだですかー2><。

凪瀬さん:む、むむ?よく考えれば分かる…かも?

2リットルさん:コ、コンパイルできません><。
も~ひと手間頂きたかったかもしれません。

dolanさん:くっ、黒龍さんのと同じく、どうしてこれでうまく
いくのか分からない。えーとC教本は…。

みきぬさん:今やっと理解しました。基数2指定で文字列変換し、
正規表現でマッチングですか。重そうだけどありだと思いますw。

Piz&Yuminaさん:なんとなくやりたいことは想像がつくのだけれど、
構文がよく分からなくて解読がー。基数2で文字列変換したデータを
ソースに…えーとえーと、あー><。

επιστημηさん:そーきたかー><ぱぱっとすーぎー。

投稿日時 : 2008年7月18日 0:04

コメントを追加

# re: ぱぱっと書かせるプログラムの問題、の続き 2008/07/18 0:27 NyaRuRu

POPCNT を使っている人がいなかったので.

// for Visual C++ 2008
#include <intrin.h>
bool isAPo2(int num){return __popcnt(num) == 1;}

# 2 の階乗かどうかを判定するの解説 2008/07/18 3:05 黒龍's Blog

2 の階乗かどうかを判定するの解説

# 2 の階乗かどうかを判定してみるテスト 2008/07/18 8:01 dolan(どらん)日記

2 の階乗かどうかを判定してみるテスト

# 2 の階乗かどうかを判定してみるテスト 2008/07/18 8:02 dolan(どらん)日記

2 の階乗かどうかを判定してみるテスト

# 2 のべき乗かどうかを判定するの解説 2008/07/18 9:46 黒龍's Blog

2 のべき乗かどうかを判定するの解説

# re: ぱぱっと書かせるプログラムの問題、の続き 2008/07/18 10:05 れい

2進表記でビットが1つしか立ってないことと累乗であることが同値というのが本質なので、

分かりやすさからすればεπιστημηさんとかNyaRuRuさん、Hirotowさんのがよいですし、
速度の点ではNyaRuRuさんのが最高。

私のコードは短いですけど
最速でもなく、すごく分かりづらい。
ビットを数えるインストラクションが無いプラットフォームで、
なるべく高速に演算したい時にしか使えない。

そんなシチュエーションはめったに無い。

保守性だとか分かりやすさを求められる世界ではダメコードですよねぇ

# re: ぱぱっと書かせるプログラムの問題、の続き 2008/07/18 10:22 みきぬ

もしこの処理が実際に必要になったら、2リットルさんの案をもう1歩進めて、初回実行時に2のべき乗を配列にしたものを作成して、それと照合する方法を使おうかな、と思った。

# 2^nの判定--ぱぱっと書かせるプログラムの問題 2008/07/18 13:18 Ognacの雑感

2^nの判定--ぱぱっと書かせるプログラムの問題

# re: ぱぱっと書かせるプログラムの問題、の続き 2008/07/18 15:12 2リットル

あぁ!
立っている最大のbitをみるとわかりやすいですね。
エレガントだー。

◇ x != 2-nの場合
x = 2^n + k (0 < k < 2^n)
x-1 = 2^n + k-1
->両方ともn+1番目のbitは立っている。

# re: ぱぱっと書かせるプログラムの問題、の続き 2008/07/25 0:41 石掘る猫

>れいさん
まさしく、そのとおりですね。毎回違うマイコンを使い、
かつ速度が優先される組込み系だからこそだと思います。

>みきぬさん
要素数が十分に少ないので、それが一番直感的ですよね。

>2リットルさん
う~ん、何を指している式なのかわかりません(@w@。
最近思考の持久力がぁ(。ω。分かるまで考える気力がっ。

タイトル
名前
URL
コメント