/**
* Returns a number between kLowerBound and kUpperBound
* e.g.: Wrap(-1, 0, 4); // Returns 4
* e.g.: Wrap(5, 0, 4); // Returns 0
*/
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
// Suggest an implementation?
}
14 に答える
の符号は、とが両方とも負でないa % b
場合にのみ定義されます。a
b
int Wrap(int kX, int const kLowerBound, int const kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
kX += range_size * ((kLowerBound - kX) / range_size + 1);
return kLowerBound + (kX - kLowerBound) % range_size;
}
以下は、mod演算子の実装とは独立して機能するはずです。
int range = kUpperBound - kLowerBound + 1;
kx = ((kx-kLowerBound) % range);
if (kx<0)
return kUpperBound + 1 + kx;
else
return kLowerBound + kx;
他のソリューションに対する利点は、単一の%(つまり除算)のみを使用することです。これにより、非常に効率的になります。
注(トピック外):
これは良い例です。範囲内にない最初の要素を上限として間隔を定義することが賢明な場合があります(STLイテレーターなど)。この場合、両方の「+1」が消えます。
最速のソリューション、柔軟性が最も低い:ハードウェアでラッピングを行うネイティブデータ型を利用します。
整数をラップするための絶対的に最速の方法は、データがint8 / int16/int32または任意のネイティブデータ型にスケーリングされていることを確認することです。次に、データをラップする必要がある場合、ネイティブデータ型はハードウェアで実行されます。ここで見られるどのソフトウェアラッピング実装よりも非常に痛みがなく、桁違いに高速です。
ケーススタディの例として:
これは、sin/cos実装のルックアップテーブルを使用して実装されたsin/cosの高速実装が必要な場合に非常に役立つことがわかりました。基本的に、INT16_MAXがpiで、INT16_MINが-piになるようにデータをスケーリングします。次に、行く準備ができていますか。
ちなみに、データをスケーリングすると、通常は次のような前払いの有限計算コストが追加されます。
int fixedPoint = (int)( floatingPoint * SCALING_FACTOR + 0.5 )
intをint8_t/int16_t/int32_tのような他の必要なものと自由に交換してください。
次の最速のソリューション、より柔軟:可能であれば、代わりにmod操作が遅くなります。ビットマスクを使用してみてください。
私がスキミングしたソリューションのほとんどは機能的に正しいです...しかし、それらはmod操作に依存しています。
modの動作は、本質的にハードウェアの除算を行っているため、非常に低速です。modと除算が遅い理由の素人の説明は、除算演算をいくつかの擬似コードfor(quotient = 0;inputNum> 0;inputNum -= divisor) { quotient++; }
(商と除数のdef )と同一視することです。ご覧のとおり、除数に比べて数値が小さい場合、ハードウェア除算は高速になる可能性がありますが、除数よりもはるかに大きい場合、除算もひどく遅くなる可能性があります。
データを2の累乗にスケーリングできる場合は、1サイクルで実行されるビットマスクを使用でき(すべてのプラットフォームの99%で)、速度の向上は約1桁(少なくとも2または3倍速い)。
ラッピングを実装するためのCコード:
#define BIT_MASK (0xFFFF)
int wrappedAddition(int a, int b) {
return ( a + b ) & BIT_MASK;
}
int wrappedSubtraction(int a, int b) {
return ( a - b ) & BIT_MASK;
}
#defineを実行時のものにしてください。また、ビットマスクを自由に調整して、必要な2の累乗にします。0xFFFFFFFFまたは2の累乗のように、実装することにします。
psラッピング/オーバーフロー条件をいじるときは、固定小数点処理について読むことを強くお勧めします。私は読むことをお勧めします:
この投稿を見逃さないでください。:)
これでいいの?
int Wrap(N,L,H){
H=H-L+1; return (N-L+(N<L)*H)%H+L;
}
これは負の入力に対して機能し、L が H より小さい限り、すべての引数が負になる可能性があります。
背景... (H
ここに再利用された変数があり、 original に設定されていることに注意してくださいH-L+1
)。
インクリメント時に使用していましたが(N-L)%H+L
、数か月前に C の学習を開始する前に使用した Lua とは異なり、下限を下回る入力を使用した場合、これは機能しません。負の入力は気にしません。(Lua は C でビルドされていますが、それが何をしているのかわかりませんし、おそらく高速ではないでしょう...)
C は true=1 および false=0 と定義されているように見えるので、+(N<L)*H
makeに追加することにしました。(N-L+(N<L)*H)%H+L
私にとっては十分に機能し、元の質問にきちんと答えているようです。MOD 演算子 % を使用せずに驚くほど高速にする方法を誰かが知っている場合は、実行してください。今はスピードは必要ありませんが、いずれ必要になるでしょう。
編集:
がよりN
も少ない場合、その関数は失敗しますが、これはそうではありません。L
H-L+1
int Wrap(N,L,H){
H-=L; return (N-L+(N<L)*((L-N)/H+1)*++H)%H+L;
}
どのシステムでも整数範囲の負の極値で壊れると思いますが、ほとんどの実用的な状況で機能するはずです。余分な乗算と除算が追加されますが、それでもかなりコンパクトです。
(このスレッドの新しい投稿で、はるかに優れた方法を思いついたので、この編集は完成用です。)
カラス。
個人的には、範囲が排他的で、除数が正の値に制限されている場合、これらのタイプの関数のソリューションがよりクリーンになることがわかりました。
int ifloordiv(int x, int y)
{
if (x > 0)
return x / y;
if (x < 0)
return (x + 1) / y - 1;
return 0
}
int iwrap(int x, int y)
{ return x - y * ifloordiv(x, y);
}
統合。
int iwrap(int x, int y)
{
if (x > 0)
return x % y;
if (x < 0)
return (x + 1) % y + y - 1;
return 0;
}
同じ家族。なぜだめですか?
int ireflect(int x, int y)
{
int z = iwrap(x, y*2);
if (z < y)
return z;
return y*2-1 - z;
}
int ibandy(int x, int y)
{
if (y != 1)
return ireflect(abs(x + x / (y - 1)), y);
return 0;
}
範囲機能は、すべての機能に実装できます。
// output is in the range [min, max).
int func2(int x, int min, int max)
{
// increment max for inclusive behavior.
assert(min < max);
return func(x - min, max - min) + min;
}
私の他の投稿は厄介なものになり、その「修正」乗算と除算はすべて手に負えなくなりました。Martin Stettner の投稿と、私自身の の開始条件を見て、次の(N-L)%H+L
ことを思いつきました。
int Wrap(N,L,H){
H=H-L+1; N=(N-L)%H+L; if(N<L)N+=H; return N;
}
整数範囲の極端な負の端で、他のものと同じように壊れますが、より速く、読みやすくなり、それに忍び寄る他の不快感を回避します。
カラス。
実際、 -1 % 4 は私が使用したすべてのシステムで -1 を返すため、単純な mod ソリューションは機能しません。私は試してみます:
int range = kUpperBound - kLowerBound +1;
kx = ((kx - kLowerBound) % range) + range;
return (kx % range) + kLowerBound;
kx が正の場合、変更して範囲を追加し、元に戻して追加を元に戻します。kx が負の場合、mod を実行し、range を追加してそれを正にしてから、もう一度 mod を実行しますが、これは何もしません。
最も一般的なケースであるlowerBound=0、upperBound=N-1へのエントリポイントを提供します。そして、一般的なケースではこの関数を呼び出します。私がすでに範囲内にいる場合、modの計算は行われません。これは、upper> = lower、またはn>0を想定しています。
int wrapN(int i,int n)
{
if (i<0) return (n-1)-(-1-i)%n; // -1-i is >=0
if (i>=n) return i%n;
return i; // In range, no mod
}
int wrapLU(int i,int lower,int upper)
{
return lower+wrapN(i-lower,1+upper-lower);
}
私はこの解決策を提案します:
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
int d = kUpperBound - kLowerBound + 1;
return kLowerBound + (kX >= 0 ? kX % d : -kX % d ? d - (-kX % d) : 0);
}
演算子のif-then-elseロジックは、の両方のオペランドが非負?:
であることを確認します。%
ある程度の対称性があり、kXが範囲内にある場合、変更されずに返されることも明らかな回答。
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
return kX + range_size * ((kLowerBound - kX) / range_size + 1);
if (kX > kUpperBound)
return kX - range_size * ((kX - kUpperBound) / range_size + 1);
return kX;
}
私もこの問題に直面しました。これが私の解決策です。
template <> int mod(const int &x, const int &y) {
return x % y;
}
template <class T> T mod(const T &x, const T &y) {
return ::fmod((T)x, (T)y);
}
template <class T> T wrap(const T &x, const T &max, const T &min = 0) {
if(max < min)
return x;
if(x > max)
return min + mod(x - min, max - min + 1);
if(x < min)
return max - mod(min - x, max - min + 1);
return x;
}
それが良いかどうかはわかりませんが、この問題についてGoogle検索を行ったときにここに誘導され、上記の解決策が私のニーズに欠けていることがわかったので、共有したいと思いました. =)
負の kX の場合、以下を追加できます。
int temp = kUpperBound - kLowerBound + 1;
while (kX < 0) kX += temp;
return kX%temp + kLowerBound;
拡張メソッドを使用しない理由。
public static class IntExtensions
{
public static int Wrap(this int kX, int kLowerBound, int kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
kX += range_size * ((kLowerBound - kX) / range_size + 1);
return kLowerBound + (kX - kLowerBound) % range_size;
}
}
使用法:currentInt = (++currentInt).Wrap(0, 2);