2010-02-17 12 views
6

Desidero ridurre un valore di uno e se raggiunge zero, impostarlo sul valore massimo. C'è un modo per farlo via matematica senza ricorrere a if (n-1 == 0) { n = max; }C'è un modo per implementare questa logica booleana molto semplice usando solo gli operandi matematici (come la mod)?

Lo scenario opposto di aumentare un valore di uno e impostarlo su zero quando è maggiore di max può essere facilmente ottenuto utilizzando n = (n + 1) % (max + 1);. Inoltre, questo è ancora meglio visto che puoi aumentare di qualsiasi importo (non solo uno) e continuerà a "avvolgere" correttamente.

Grazie per le risposte finora. Per intenderci, intendevo senza logica booleana (se/else) o operatori booleani (!, & &, ecc.). Ero solo curioso di sapere come farlo. La risposta corretta sotto rende davvero illeggibile finchè viene fornito un commento? Sarebbe necessario usarlo per il caso più generale per sottrarre un numero arbitrario e aspettarsi il giusto avvolgimento.

+0

Ozan fa una buona domanda qui sotto. Per curiosità, questo è solo un puzzle logico, o c'è una ragione per cui qualcuno dovrebbe evitare i costrutti del linguaggio di base? – MightyE

+0

Vedi sopra. Usare il if like funziona solo se ne stai sottraendo uno, e non se stai sottraendo un numero arbitrario e ti aspetti lo stesso tipo di wrap-around di mod. – GreenieMeanie

risposta

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

Questo risponde alla domanda e ne solleva una nuova: perché sostituire una semplice istruzione con una complicata che fa la stessa cosa? – Ozan

1

Il problema è che l'operatore % in C restituisce un risultato negativo di fronte a un dividendo negativo. Questo spesso non è ciò che è necessario.

Se si desidera una funzione mod in grado di soddisfare -1 mod n == n-1, poi, in C, è necessario effettuare le seguenti operazioni:

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

È quindi possibile fare

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

per decrementare n e hanno avvolgere intorno a max quando n==0. Si noti che, come per il caso di incremento, questo funzionerà per dimensioni di step arbitrarie.

+1

In realtà è peggio: l'operatore '%' in C può restituire un risultato negativo * o * positivo quando il dividendo è negativo. Tutto ciò che è richiesto è che tu possa mettere i risultati da '/' e '%' di nuovo insieme nel numero originale. –

+0

@Jerry: Era vero sotto C89.Tuttavia, lo standard C corrente richiede che la divisione intera troncino verso zero. Insieme al requisito che hai menzionato, questo specifica completamente l'operatore '%'. –

1

Ci sono abbastanza variazioni matematiche tra le lingue che dubito che ci sia un modo agnostico per farlo. C'è semplicemente troppa variazione nel modo in cui le lingue scrivono espressioni per una singola tecnica di base per lavorare con ogni lingua possibile.

Se scegli una lingua specifica, ci sono buone possibilità che sia possibile. Ad esempio, in C o C++, potresti fare qualcosa del tipo: n = (n-1) + ((n-1) == 0) * max;

Nel caso ti interessi come funziona: in C, un confronto (== in questo caso) produce un risultato di 0 per falso, e 1 per vero . Quindi, quello che stiamo facendo è aggiungere max * 0 quando/se n-1 != 0 e max * 1 quando/se n-1 == 0.

-1

Perché non confrontare solo a 1?

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

+0

Quello che ho chiesto nella domanda era un'alternativa al confronto come sei. – GreenieMeanie

5

Se stai facendo questo puramente per motivi di prestazioni, allora vorrei consigliare contro di essa. % di solito è un'operazione piuttosto costosa sulla maggior parte delle architetture: un semplice confronto, diramazione e aggiunta di solito sono più efficienti. Ovviamente si applicano i soliti avvertimenti sull'ottimizzazione speculativa/prematura.

+3

E il solito avvertimento che le architetture variano un po ', e cambiano abbastanza velocemente e non in modo prevedibile. Ho rinunciato a cercare di indovinare qualsiasi cosa tranne la localizzazione della cache. –

+1

Ho provato la compilazione (con ottimizzazione completa) e il disassemblaggio, e sembra che tu abbia ragione: non solo il modo originale è più corto, ha meno diramazioni (!). Credo che nell'hardware sia necessario fare una divisione per ottenere il modulo, e la divisione non è economica o facile. – Ken

0

Potrebbe esserci un modo migliore, ma penso che lo n = max - (max - n + 1) % (max + 1) funzioni. Suppongo che tu voglia includere 0 a entrambe le estremità poiché per l'espressione di incremento che fai includi 0.

-1

in qualsiasi lingua di valutazione logica di corto circuito (la maggior parte delle lingue moderne) si può fare qualcosa di simile:

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

Si potrebbe ottenere il bulbo oculare peloso da alcuni dei vostri colleghi se la tua squadra non è abituato a utilizzare & & come operatore if-then.

Per fare l'incremento loop in avanti:

++n>max && n=1;

Questi esempi stanno assumendo un contatore 1-indicizzato dal momento che il problema sembra supporre che. 0-indicizzati equivalenti sono:

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

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

+0

Il --n non funzionerebbe per un tipo senza segno che presumibilmente è la ragione per cui sta evitando il confronto. – Joel

+0

Funzionerebbe ancora per il caso con un solo indice. Per il caso senza segno indicizzato zero, questo dovrebbe funzionare se la lingua non ha errori sull'intero underflow: 'n - == 0 && n = max-1'. Hai ragione, PO era indipendente dal linguaggio, quindi ho pensato che int (anche i miei casi si sarebbero interrotti se avessi usato il float, ecc.). – MightyE

0

In realtà si può fare anche

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

Che ne dite di questo:!
n = n * max + (!! n) * n;

0

Re: nessun booleani
bene, attraverso la magia della divisione intera (C-style), la mia risposta precedente può essere scritta come:
n = ((max-n)/max) * max + ((max + n-1)/max) * n;

0

Solo per chiedere: perché vuoi evitare un'operazione booleana?

Se si desidera evitare il codice condizionale nell'applicazione, è possibile utilizzare espressioni booleane memorizzate in valori booleani. Quelli saranno mappati alle istruzioni SETcc su un i386 e suppongo che esistano installazioni analogiche su altri ISA.

In questo caso è possibile utilizzare un espressione booleana e avere ancora il codice non condizionale e si potrebbe usare:

Partendo dal presupposto che un risultato booleano del vero è uguale al numero ordinale 1 (questo è il caso nel codice Delphi) e un valore booleano false pari a 0 si potrebbe scrivere

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

non votare questa giù perché ho dato una risposta con operazioni booleane. Voglio solo verificare se questa è una soluzione differnet al problema di fondo.

Ancora: Penso che il codice condizionale sia molto più leggibile.

Problemi correlati