2010-02-17 2 views
6

Je souhaite diminuer une valeur de un et, si elle atteint zéro, la définir sur la valeur maximale. Existe-t-il un moyen de le faire via les maths sans avoir recours à if (n-1 == 0) { n = max; }Existe-t-il un moyen d'implémenter cette logique booléenne très simple en utilisant uniquement des opérandes mathématiques (tels que mod)?

Le scénario inverse consistant à augmenter une valeur de un et à la définir sur zéro quand il est supérieur à max peut facilement être obtenu en utilisant n = (n + 1) % (max + 1);. En outre, c'est encore mieux puisque vous pouvez augmenter de n'importe quelle quantité (pas seulement une) et il va encore "envelopper" correctement.

Merci pour les réponses à ce jour. Pour être clair, je voulais dire sans aucune logique booléenne (if/else) ou opérateurs booléens (!, & &, etc) du tout. J'étais juste curieux de savoir comment faire ça. Est-ce que la bonne réponse ci-dessous la rend vraiment plus illisible tant qu'un commentaire est fourni? Il serait nécessaire d'utiliser cela pour le cas plus général de soustraire un nombre arbitraire et d'attendre le bon enroulement.

+0

Ozan pose une bonne question ci-dessous. Par curiosité, est-ce juste un casse-tête logique, ou y a-t-il une raison pour laquelle quelqu'un aurait besoin d'éviter les constructions de langage de base? – MightyE

+0

Voir ci-dessus. L'utilisation de l'option if ne fonctionne que si vous en soustrayez une, et non si vous soustrayez un nombre arbitraire et attendez le même type de wrap-around que mod fournit. – GreenieMeanie

Répondre

6
n = max - ((max - n +1)%max) 
+7

Cela répond à la question et en soulève une nouvelle: pourquoi remplacer une simple déclaration par une simple qui fait la même chose? – Ozan

1

Le problème est que l'opérateur % en C renvoie un résultat négatif face à un dividende négatif. Ce n'est souvent pas ce qui est nécessaire.

Si vous voulez une fonction mod qui répondra à -1 mod n == n-1, puis, en C, vous devez faire ce qui suit:

int mod(int a, int b) 
{ 
    return (a%b + b) % b; 
} 

Vous pouvez alors faire

n=mod(n-1, max+1); 

à décrémenter n et ont il envelopper à max lorsque n==0. Notez que, comme pour le cas d'incrément, cela fonctionnera pour des tailles de pas arbitraires.

+1

C'est en fait pire: l'opérateur '%' dans C peut renvoyer un résultat négatif * ou * positif lorsque le dividende est négatif. Tout ce qui est requis est que vous pouvez mettre les résultats de '/' et '%' ensemble dans le numéro d'origine. –

+0

@Jerry: C'était vrai sous C89.Cependant, la norme C actuelle exige que la division entière soit tronquée vers zéro. Conjointement avec l'exigence que vous mentionnez, ceci spécifie entièrement l'opérateur '%'. –

1

Il y a assez de variations dans les mathématiques entre les langues que je doute qu'il y ait un moyen agnostique de faire cela. Il y a simplement trop de variations dans la façon dont les langues écrivent des expressions pour une technique unique de base pour travailler avec tous les langages possibles.

Si vous choisissez une langue spécifique, il y a de fortes chances que ce soit possible. Par exemple, en C ou C++, vous pouvez faire quelque chose comme: n = (n-1) + ((n-1) == 0) * max;

Dans le cas où cela vous intéresse comment cela fonctionne: en C, une comparaison (== dans ce cas) produit un résultat de 0 pour faux, et 1 pour vrai . Donc ce que nous faisons est d'ajouter max * 0 quand/si n-1 != 0, et max * 1 quand/si n-1 == 0.

-1

Pourquoi ne pas comparer à 1?

if(n==1) { n = max; }

+0

Ce que j'ai demandé dans la question était une alternative à la comparaison que vous êtes. – GreenieMeanie

5

Si vous faites cela uniquement pour des raisons de performance alors je le déconseille. % est généralement une opération assez coûteuse sur la plupart des architectures - une comparaison simple, une branche et une addition seront généralement plus efficaces. Bien sûr, les mises en garde habituelles concernant l'optimisation spéculative/prématurée s'appliquent.

+3

Et la mise en garde habituelle que les architectures varient beaucoup, et changent assez rapidement et pas de manière prévisible. J'ai renoncé à essayer de deviner quoi que ce soit, sauf la localité de cache. –

+1

J'ai essayé de compiler (avec optimisation complète) et de désassembler, et on dirait que vous avez raison: non seulement la voie originale est plus courte, mais elle a moins de branches (!). Je suppose que dans le matériel, vous devez faire une division pour obtenir le modulo, et la division n'est pas bon marché ou facile. – Ken

0

Il pourrait y avoir un meilleur moyen mais je pense que n = max - (max - n + 1) % (max + 1) fonctionne. Je suppose que vous voulez inclure 0 aux deux extrémités puisque pour votre expression d'incrément vous incluez 0.

-1

Dans toute langue d'évaluation logique court-circuit (la plupart des langues modernes), vous pouvez faire quelque chose comme ceci:

--n<=0 && n=max;

Vous pouvez obtenir le globe oculaire poilu de certains de vos collègues de travail si votre équipe n'est pas habitué à utiliser & & en tant qu'opérateur if-then.

Pour faire l'avant incrément en boucle:

++n>max && n=1;

Ces exemples sont en supposant un compteur 1 indexé depuis votre question semble supposer que. 0 indexées équivalents sont:

--n<0 && n=max-1;

++n>=max && n=0;

+0

Le --n ne fonctionnerait pas pour un type non signé, ce qui est probablement la raison pour laquelle il évite la comparaison. – Joel

+0

Cela fonctionnerait toujours pour le cas indexé. Pour le cas non signé indexé par zéro, cela devrait fonctionner si la langue ne fait pas erreur sur le sous-flux entier: 'n - == 0 && n = max-1'. Vous avez raison, OP était indépendant de la langue, donc j'ai juste supposé int (mes cas seraient également cassés si vous utilisiez float, etc). – MightyE

0

En fait, vous pouvez le faire aussi

n = (n - 1) + ((!(n - 1)) * max); 
0

Qu'en est-ce:
n = n * max + (!! n) * n;

0

Re: booléens
Eh bien, grâce à la magie de la division entière (style C), ma réponse précédente peut être écrite comme:
n = ((max-n)/max) * max + ((max + n-1)/max) * n;

0

Juste pour demander: pourquoi voulez-vous éviter une opération booléenne?

Si vous souhaitez éviter le code conditionnel dans votre application, vous pouvez utiliser des expressions booléennes stockées dans des valeurs booléennes. Ceux-ci seront mappés aux instructions SETcc sur un i386 et je suppose que des instructions analogiques existent sur d'autres ISA.

Dans ce cas, vous pouvez utiliser une expression booléenne et ont encore du code non conditionnel et vous pouvez utiliser:

Dans l'hypothèse où un résultat booléen de vrai égale à la ordinal 1 (ce qui est le cas dans le code Delphi) et une valeur booléenne de faux égaux à 0 vous pouvez écrire

next := current - 1 + ((1 + max) and -Ord(current = 0)); 

ne votez pas ce bas parce que j'ai donné une réponse aux opérations booléennes. Je veux juste vérifier s'il s'agit d'une solution différente du problème sous-jacent.

Encore: Je pense que le code conditionnel est beaucoup plus lisible.