web-dev-qa-db-ja.com

C ++での負の数の係数

私は以下の繰り返し関係のプログラムを書いています:

An = 5An-1 - 2An-2  - An-3 + An-4

出力はAnswerモジュラス10 ^ 9 + 7.である必要があります。私はこれに対してブルートフォースアプローチを次のように記述しました...

long long int t1=5, t2=9, t3=11, t4=13, sum;
while(i--)
{
    sum=((5*t4) - 2*t3 - t2 +t1)%MOD;
    t1=t2;
    t2=t3;
    t3=t4;
    t4=sum;
}
printf("%lld\n", sum);

どこ MOD= 10^9 +7すべてが正しいように見えますが、一部の値について否定的な回答が得られます。この問題のため、正しい解決策を見つけることができません... PlzがModulus

23
nitish712

問題は、%演算子が「モジュロ演算子」ではなく、次の等式を持つ「除算剰余」演算子であることです。

(a/b)*b + a%b == a    (for b!=0)

したがって、整数の除算がゼロに丸められる場合(C99およびC++ 11以降に義務付けられていると思います)、-5/4は-1になり、

(-5/4)*4 + -5%4 == -5
  -1  *4    -1  == -5

正の結果を得るには(剰余演算の場合)、余りが負の場合に除数を追加するか、次のようにします。

long mod(long a, long b)
{ return (a%b+b)%b; }
34
sellibitze

@sellibitzeと@liquidblueoceanの回答で_%_を2回使用すると、_%_は、bの1つの減算か、またはなしに要約されるため、一般的に遅くなる可能性があります。実際に確認させてください...

_int main(int argc, char **argv) {
    int a = argc;    //Various tricks to prevent the
    int b = 7;       //compiler from optimising things out.
    int c[10];       //Using g++ 4.8.1
    for (int i = 0; i < 1000111000; ++i)
        c[a % b] = 3;
        //c[a < b ? a : a-b] = 3;
    return a;
}
_

別の方法として、_%_または他の行でコメントを付けると、次のようになります。

  • _%_の場合:14秒

  • _?_の場合:7秒

したがって、_%_は、私が思ったほど最適化されていません。おそらく、その最適化によってオーバーヘッドが増えるためです。

したがって、パフォーマンス上の理由から、_%_を2回使用しないことをお勧めします。

代わりに、 この答え が示唆および説明するように、これを実行します。

_int mod(int k, int n) {
    return ((k %= n) < 0) ? k+n : k;
}
_

負のnに対しても適切に機能させるには、もう少し作業が必要です ですが、ほとんど必要ありません。

8
Evgeni Sergeev

%を負の値を処理する関数に置き換えるだけです。

long long int mod(long long int a, long long int b) {
    long long int ret = a % b;
    if (ret < 0)
        ret += b;
    return ret;
}

編集:データ型をlong long intに変更しました。

5
rasmus

現在、ここで1回限りの追加が式に含まれているすべての回答は、abs(a)> bの場合は誤りです。これまたは類似のものを使用します。

int modulo (int a, int b) { return a >= 0 ? a % b : ( b - abs ( a%b ) ) % b; }
3
G Huxley

他の人が言ったように%modではなく、単なる剰余演算子です。ただし、mod/remainder操作は、このような繰り返し関係を通じて正しく分散するので、このように最終的な解が正になるように調整すると、

if (sum < 0) { sum = sum + MOD; }

次に、正しい答えを得る必要があります。この方法で行うことの利点は、ループごとの反復ごとに1つ少ない関数呼び出しや分岐を導入することです。 (コンパイラがどれほど賢いかによって、どちらが問題になるかは重要ではありません)。

1