web-dev-qa-db-fra.com

Module à nombres négatifs en C ++

J'ai écrit un programme pour la relation de récurrence suivante:

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

La sortie devrait être le module de réponse 10 ^ 9 + 7 .. J'ai écrit une approche par force brute pour celle-ci comme suit ...

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 Tout semble vrai .. mais j'obtiens une réponse négative pour certaines valeurs .. et à cause de ce problème, je n'arrive pas à trouver la bonne solution ... Aidez Plz sur le bon endroit pour garder le Modulus

23
nitish712

Le fait est que l'opérateur% n'est pas "l'opérateur modulo" mais l'opérateur "division reste" avec l'égalité suivante

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

Donc, si dans le cas où votre division entière arrondit vers zéro (ce qui est obligatoire depuis C99 et C++ 11, je pense), -5/4 sera -1 et nous avons

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

Afin d'obtenir un résultat positif (pour l'opération modulo), vous devez ajouter le diviseur au cas où le reste était négatif ou faire quelque chose comme ceci:

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

En utilisant % une deuxième fois dans les réponses de @ sellibitze et @ liquidblueocean ne sera probablement pas aussi lente que % a tendance à être générale, car elle se résume à une soustraction de b ou à aucune. En fait, permettez-moi de vérifier que ...

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;
}

Vous pouvez également commenter la ligne avec % ou l'autre ligne, on obtient:

  • Avec %: 14 secondes

  • Avec ?: 7 secondes

Alors % n'est pas aussi optimisé que je ne le pensais. Probablement parce que cette optimisation ajouterait des frais généraux.

Par conséquent, il vaut mieux ne pas utiliser % deux fois, pour des raisons de performances.

Au lieu de cela, comme cette réponse suggère et explique, faites ceci:

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

Cela prend n peu plus de travail si vous voulez qu'il fonctionne correctement pour les négatifs n aussi , mais ce n'est presque jamais nécessaire.

8
Evgeni Sergeev

Remplacez simplement % par une fonction qui gère les valeurs négatives:

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;
}

EDIT: a changé le type de données en long long int.

5
rasmus

Toutes les réponses actuellement ici qui ont un ajout ponctuel dans leur formule sont fausses lorsque abs (a)> b. Utilisez ceci ou similaire:

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

Comme d'autres l'ont dit % n'est qu'un opérateur restant plutôt que mod. Cependant, l'opération mod/reste se distribue correctement via des relations de récurrence comme celle-ci, donc si vous ajustez simplement votre solution finale pour qu'elle soit positive, comme ceci,

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

alors vous devriez obtenir la bonne réponse. L'avantage de procéder de cette façon est que vous introduisez un appel de fonction et/ou une branche de moins par itération de boucle. (Ce qui peut ou non avoir de l'importance selon le niveau d'intelligence de votre compilateur).

1
Michael Anderson