- ベストアンサー
最大値と最小値の間を循環する変数の最適な方法
- 最大値と最小値の間を循環する変数の実装方法について悩んでいます。
- 現在のコードでは、if文を使用して加算と減算を判定していますが、もっと簡潔な方法はないか考えています。
- 例えば、counter %= (MAX + 1) のような処理を実装すれば、加算の場合は問題なく動作しますが、減算の場合にはうまく動作しません。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
MIN≦counter≦MAXで循環させるのに、Xで割った余りを使いたいのなら、 0~X-1の範囲になるように変形する必要があります。 全辺から MINを引くと 0≦counter-MIN≦MAX-MIN なので、 counter - MIN を MAX-MIN +1で割った余りを使うことになります。 引いたMINは改めて足せば元の範囲になります。 加算 counter = MIN + ( ( counter - MIN ) +1 ) % (MAX - MIN + 1) ; 減算:負の%演算はややこしいので、1周期分加えて正の値にする counter = MIN + ( ( counter - MIN ) - 1 + (MAX - MIN + 1)) % (MAX - MIN + 1) ; 自分で書いといてなんですが、正直、わかりずらいです。 それに、整数演算の中では、割り算はもっとも時間のかかるものです。その意味ではこの方法は効率的とは言えません。 値の変化が任意(+50とか*15とかする)なら、割り算を使うのが効果的かもしれませんが。 私が作るなら、この質問にあるようなif文を使うか、三項演算子を使うか、ですね。 加算 counter = (counter == MAX) ? MIN : (counter + 1) ; 減算 counter = (counter == MIN) ? MAX : (counter - 1) ;
その他の回答 (5)
- hiropuri
- ベストアンサー率55% (24/43)
とりあえず、最後のelseは不要ですよね。 それと、plus と minus はそれぞれ個別でbool定義されて いますが、それは必須でしょうか? 無駄なような気が……? 既に色々な手法が提案されていますので、自分は「その他案」を。 例えばint型配列にて{ 1, -1} を定義すれば、以下の形が可能です。 c が 0 ならインクリメント、1 ならデクリメントされるという事ですね。 counter += tbl[c]; if (counter < MIN) counter = MAX; if (counter > MAX) counter = MIN; 判り易いので自分はこんな風に書く時もあるのですが、これを 人にオススメしていいのかは判りません……(^^;
お礼
御回答ありがとうございます。 質問の本筋以外は、例を示す為に適当に書いたものなので、深く突っ込まないで頂けるとありがたいです。 この方法も面白いですね。 ややトリッキーな感じなので、個人で書くプログラム以外では使用しづらいですが、知識として蓄えておきます。
- 正親町(@Ohgimachi)
- ベストアンサー率43% (110/252)
FIFO(循環バッファ)のようなものであれば バッファの大きさを2の階乗にすると char buffer[256]; unsigned char read_p, write_p; //書き込み buffer[write_p++] = data; //読み込み if (read_p != write_p) data = buffer[read_p++]; と書けます。バッファが128バイトであれば char buffer[2^7]; unsigned int read_p, write_p; //書き込み buffer[write_p++] = data; write_p &= (sizeof(buffer) - 1); //読み込み if (read_p != write_p) { data = buffer[read_p++]; read_p &= (sizeof(buffer) - 1); } となり、if文が省略できて高速です。 またCPUによっては、バッファのサイズが256でもインデックスをunsigned intにして128と同様に記述した方が速い場合があります。 バッファの定義をアセンブラで記述して、アライメントを2の階乗に揃えた場合、インデックスではなくてポインターが使用できるのでより高速になります。
お礼
御回答ありがとうございます。 想定した回答よりも随分高度なものですが、参考にさせて頂きます。
- Tacosan
- ベストアンサー率23% (3656/15482)
正直なところ, 今のコードのままでいいんじゃないの? 無駄に凝るよりシンプルでいいと思うんだけどなぁ.... まあ「全ての実行パスで所要クロック数を統一したい」というならそれはそれでいいけど, かえって面倒な感じしかしない. ちなみに「MAX 以上 MIN 以下の間を動く」んだったら, counter %= MAX+1; じゃダメだよね.
お礼
御回答ありがとうございます。 確かに、実際の現場では、わかりやすさを優先してこのように書くかもしれません。 ただ、知っているのと知らないのとでは大違いなので、質問自体は意義があると思いたです。
- asuncion
- ベストアンサー率33% (2127/6289)
plus minus MAX MIN の意味がわかりませんので、一応動くといわれましても にわかに信じがたいです。
補足
御回答ありがとうございます。 質問とは直接関係ない部分だと思ったので、質問をシンプルにするつもりで省略してしまいました。 plus・minusには特に意味はなく、単に真になったらインクリメントもしくはデクリメントするといった程度の意味です。 MINとMAXは最小値と最大値で、counterを0~9の間で循環させたいなら、MINは0でMAXは9になります。 適当なところでdefineされているイメージです。 以上、捕捉になっているでしょうか。
- Wr5
- ベストアンサー率53% (2173/4061)
増やす場合… counter = (counter + 1) % MAX; 減らす場合… counter = (counter + (MAX - 1)) % MAX; ってコトですか?
お礼
御回答ありがとうございます。 この方法ならば、大分すっきり書けます。 実際のcounterの最大値は(MAX-1)になるのですよね? ただし、それはどうとでも対処できそうです。
お礼
御回答ありがとうございます。 なるほど、三項演算子という手段がありましたか。 これが総合的に見て一番優れている気がします。 割り算を使用する方法も、適用するかどうかは別として、自力で考えつく位にはなりたいものです。