Determinare se un numero è un multiplo di dieci o all'interno di un particolare insieme di intervalli


103

Ho alcuni loop di cui ho bisogno nel mio programma. Posso scrivere lo pseudo codice ma non sono del tutto sicuro di come scriverlo logicamente.

Ho bisogno -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

Questo è per un gioco da tavolo con serpenti e scale, se ha più senso per la mia domanda.

Immagino che la prima istruzione if di cui avrò bisogno per usare il modulo, sarebbe if (num == 100%10)corretta?

Il secondo non ne ho idea. Posso scriverlo in questo modo, if (num > 10 && num is < 21 || etc)ma deve esserci qualcosa di più intelligente di quello.


16
Generalmente, la lunghezza di un buon codice è proporzionale alla lunghezza dell'inglese che descrive ciò che fa. Quindi, quando la tua "specifica" dice 11-20, 31-40, 51-60, 71-80, 91-100, puoi aspettarti che il tuo codice menzioni anche quei numeri. Se quei numeri provengono da qualche parte o sono stati generati da qualche motivo, vedi se riesci a codificare il motivo anziché i numeri.
luqui

39
@ user3419168: Al compilatore non interessa minimamente quanto sia leggibile il tuo codice; lo compilerà in una frazione di secondo. Ma per gli umani che leggono il tuo codice, le scelte che fai possono far sì che venga compreso in pochi secondi, minuti, ore o mai. Ciò impone un costo; le persone vengono pagate per leggere e comprendere il codice, quindi rendili più facili. Scrivi sempre il codice di produzione per massimizzare la leggibilità e ricorda che la concisione non migliora necessariamente le prestazioni del codice.
Eric Lippert

2
@AmadeusDrZaius - RARAMENTE ho fatto lo stesso, ma solo per sezioni critiche per le prestazioni. Il loop più stretto che si chiama 100M volte si qualifica - l'istruzione if in un gioco di serpenti e scale non lo fa. Dove tracciare il confine tra loro è una scelta personale.
Floris

2
Odio dirlo, ma avendo svolto abbastanza lavoro aziendale, con i principianti che scrivono codice reale, dovrei raccomandare di forzarlo bruto. Perché allora i nuovi ragazzi lo capiranno e non lo romperanno. triste ma vero: in alcuni casi è intelligente essere stupidi.
Richard Le Mesurier

22
Questa è una domanda decente e non voglio assolutamente togliere nulla al poster, ma questo non merita più di 500 punti. È così che finiamo con alcune delle sciocchezze che facciamo con persone con migliaia di punti che sembrano essere delle autorità qui. (Sentiti libero di spostare questo commento se appartiene altrove.)
GaTechThomas

Risposte:


86

Per il primo, per verificare se un numero è un multiplo d'uso:

if (num % 10 == 0) // It's divisible by 10

Per il secondo:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

Ma è piuttosto denso, e potresti essere meglio elencare le opzioni in modo esplicito.


Ora che hai dato un'idea migliore di quello che stai facendo, scriverei il secondo come:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

È la stessa logica, ma utilizzando la funzione abbiamo un'idea più chiara di cosa significa.


79
if((num - 1) / 10) % 2 == 1 && num < 100)- Piangerei se lo vedessi.
Daniel Kamil Kozar

32
@DanielKamilKozar, come dovresti.
Winston Ewert

2
@ user3419168, da solo ci si chiede cosa significhi nel mondo. Non dà suggerimenti su cosa nel mondo sta cercando di fare. Ecco perché nella modifica ho mostrato una versione che divide la logica in una funzione che rende più chiaro ciò che i calcoli stanno effettivamente facendo.
Winston Ewert

3
Può essere prudente affermare anche num >= 11come (1) che il limite inferiore è proscritto e (2) %su un numero negativo restituisce anche un numero negativo. (Devo ammettere che l'uso & 1qui è "più sicuro" ma presuppone anche una conoscenza aggiuntiva.)
usr2564301

2
+1 per la modifica, entra nel perché dell'elenco di intervalli e lo presenta in modo leggibile. IMO, un passo avanti sarebbe quello di avvolgere anche getRow(num) % 2 == 0in una funzione per rendere chiarissimo qual è l'intento. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Signor Mindor

40

if (num è un multiplo di 10) {do this}

if (num % 10 == 0) {
  // Do something
}

if (num è compreso tra 11-20, 31-40, 51-60, 71-80, 91-100) {do this}

Il trucco qui è cercare una sorta di comunanza tra le gamme. Ovviamente puoi sempre usare il metodo "forza bruta":

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

Ma potresti notare che, se sottrai 1da num, avrai gli intervalli:

10-19, 30-39, 50-59, 70-79, 90-99

In altre parole, tutti i numeri a due cifre la cui prima cifra è dispari. Successivamente, è necessario elaborare una formula che lo esprima. Puoi ottenere la prima cifra dividendo per 10, e puoi verificare che sia dispari controllando un resto di 1 quando dividi per 2. Mettendo tutto insieme:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Dato il compromesso tra codice più lungo ma mantenibile e codice "intelligente" più breve, sceglierei ogni volta più lungo e più chiaro. Per lo meno, se cerchi di essere intelligente, per favore, includi un commento che spieghi esattamente cosa stai cercando di realizzare.

È utile presumere che il prossimo sviluppatore che lavorerà sul codice sia armato e sappia dove vivi. :-)


7
Preferirei comunque il codice intelligente, ma lo trasformerei in codice gestibile estraendo le funzioni. Sarebbe altrettanto leggibile se quell'ultimo bit fosse detto && isTensDigitOdd(num), magari con un commento prima della definizione della funzione che spieghi cosa fa. Se esiste un tale modello, un commento che spiega il ragionamento per il modello è illuminante per la manutenibilità imo.
chris

3
Chris, questa è un'ottima strategia quando l '"intelligenza" ha un chiaro vantaggio: codice molto più breve (il che significa meno possibilità di un errore di battitura, soprattutto se cambia), o un grande miglioramento dell'efficienza. C'è quasi sempre un compromesso tra brevità, chiarezza ed efficienza e trovare un buon compromesso è una grande abilità da sviluppare. (Vedi stackoverflow.com/a/2151844/29157 per una risatina.)
Adam Liss

1
Questo è un approccio molto migliore. Molto più facile da capire rispetto al "codice intelligente" e la differenza di prestazioni è probabilmente trascurabile.
user1477388

@AdamLiss, Sì, la mia opinione è di scarso valore perché non ho avuto abbastanza esperienza per vedere le ripercussioni di queste decisioni. Sono sicuro che lo farò abbastanza presto e mi assicurerò di ottenere un secondo parere se necessario.
chris

1
Non venderti allo scoperto. Il tuo istinto è molto sensibile e sembri ansioso di continuare ad imparare. Ogni opinione è preziosa se c'è una buona ragione dietro ... e talvolta anche se non c'è. Scommetto che andrai lontano.
Adam Liss

30

Se stai usando GCC o qualsiasi compilatore che supporta intervalli di casi, puoi farlo, ma il tuo codice lo farà non sarà portabile .

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}

1
Puoi dirmi perché questo codice non è portatile?
M Sharath Hegde

8
@MSharathHegde perché richiede l'estensione GCC, che non fa parte dello standard e alcuni compilatori non la supporteranno
Bryan Chen

5
Questa è la risposta giusta, perché è immediatamente evidente qual è l'intento. Tutte quelle risposte "intelligenti" con il modulo sono un incubo di manutenzione, anche con i commenti.
smirkingman

@smirkingman In effetti è quello che ho detto nel mio commento alla domanda principale. Ci vuole solo un po 'di esperienza di nuovi programmatori in un lavoro aziendale per rendersi conto che il modo ovvio è spesso molto meglio del modo intelligente-ninja.
Richard Le Mesurier

15

Questo è per i futuri visitatori più che per un principiante. Per una soluzione più generale, simile ad un algoritmo, puoi prendere un elenco di valori iniziali e finali e controllare se un valore passato è all'interno di uno di essi:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

Per semplicità, ho usato un lambda polimorfico (C ++ 14) invece di un pairargomento esplicito . Anche questo dovrebbe probabilmente attenersi all'uso <e ==per essere coerente con gli algoritmi standard, ma funziona in questo modo finché Elemè <=definito per esso. Ad ogni modo, può essere utilizzato in questo modo:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

C'è un esempio dal vivo qui .


Ottima soluzione. Probabilmente avrei usato un singolo array, poiché puoi formattarlo con 2 numeri per riga per rappresentare le coppie.
Kevin Lam

@ HunterGuy2, punto molto buono. In realtà lo cambierò per operare su coppie perché stavo pensando solo agli iteratori zip per qualche motivo.
chris

Davvero un bel approccio stl! Lo adoro!
higuaro

5

Il primo è facile. Devi solo applicare l'operatore modulo al tuo valore num:

if ( ( num % 10 ) == 0)

Poiché C ++ valuta ogni numero diverso da 0 come vero, potresti anche scrivere:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

Per il secondo, penso che questo sia più pulito da capire:

Lo schema si ripete ogni 20, quindi puoi calcolare il modulo 20. Tutti gli elementi che vuoi saranno in una riga tranne quelli che sono divisibili per 20.

Per ottenere anche quelli, usa num-1 o meglio num + 19 per evitare di trattare con numeri negativi.

if ( ( ( num + 19 ) % 20 ) > 9 )

Questo presuppone che il modello si ripeta per sempre, quindi per 111-120 si applicherebbe di nuovo e così via. Altrimenti devi limitare i numeri a 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )

5

Con un paio di buoni commenti nel codice, può essere scritto in modo abbastanza conciso e leggibile.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }

2
Il primo commento non è necessario. Qualsiasi programmatore con un po 'di esperienza saprà che num % 10 == 0è la stessa cosa di numun multiplo di 10.
Justin

7
si ma anche i principianti leggono questo sito. Normalmente non userei quel commento nel mio codice, ma rende la risposta più chiara ai principianti che trarrebbero beneficio da questa domanda per principianti.
La-comadreja

2
Per favore, non farlo mai. In realtà riduce la leggibilità, rallentando il lettore e costringendolo a leggere tutto due volte. Qualsiasi programmatore che non lo capisce if (num % 10 == 0)significa lo stesso che // Check if it's a multiple of 10non dovrebbe mantenere il tuo codice. Questo è un noto anti-pattern.
Dawood ibn Kareem

1
@DavidWallace vedi commento sopra. Non possiamo garantire che i lettori di questo post conoscano questo anti-pattern.
La-comadreja

1
No, intendo dire che commentare ogni riga per dire cosa fa è un anti-pattern. Non voglio dire che l'uso %sia un anti-pattern; ovviamente non lo è. In realtà, supponendo che molti dei lettori di questo post siano principianti, insegnare loro questo stile di scrivere commenti sta dando un contributo negativo al loro sviluppo come programmatori.
Dawood ibn Kareem

4

Fondamentalmente hai spiegato tu stesso la risposta, ma ecco il codice per ogni evenienza.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}

2
Correggi x < 41 x > 50e metti parentesi.
101010

1
@ 40two, tecnicamente, operator&&ha una precedenza maggiore di operator||, quindi va bene, ma sono abbastanza sicuro che GCC lo avverta comunque.
chris

18
Considera l'idea di rappresentare la disuguaglianza 10 < x < 21come 10 < x && x < 21invece di x > 10 && x < 21. È più facile leggere la disuguaglianza quando è nello stesso ordine in cui la scriveresti matematicamente.
Eric Lippert

5
Questo codice è abbastanza illeggibile e dice poco sulla logica effettiva. Non mi piace questa risposta.
Dariusz

3
Sto facendo un downvoting perché hai risposto esattamente a ciò che ha fatto l'OP.
Bruno Ferreira

3

Potresti pensare troppo a questo.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

La prima riga if (x % 10)funziona perché (a) un valore che è un multiplo di 10 viene calcolato come '0', altri numeri risultano nel loro resto, (b) ifviene considerato un valore di 0 in un false, qualsiasi altro valore ètrue .

Modificare:

Per passare avanti e indietro negli anni venti, usa lo stesso trucco. Questa volta, il numero chiave è 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10restituisce un numero qualsiasi da 0 a 9 come 0, da 10 a 19 come 1e così via. Testare su pari o dispari - il & 1- ti dice se è pari o dispari. Poiché i tuoi intervalli sono effettivamente "da 11 a 20", sottrai 1 prima di eseguire il test.


1

Una richiesta di leggibilità

Anche se hai già alcune buone risposte, vorrei raccomandare una tecnica di programmazione che renderà il tuo codice più leggibile per qualche futuro lettore - che puoi essere tu in sei mesi, un collega ha chiesto di eseguire una revisione del codice, il tuo successore, .. .

Questo serve a racchiudere qualsiasi istruzione "intelligente" in una funzione che mostra esattamente (con il suo nome) cosa sta facendo. Sebbene ci sia un impatto minimo sulle prestazioni (da "overhead di chiamata di funzione") questo è veramente trascurabile in una situazione di gioco come questa.

Lungo la strada puoi disinfettare i tuoi input, ad esempio testare valori "illegali". Quindi potresti ritrovarti con un codice come questo - vedi quanto è più leggibile? Le "funzioni di supporto" possono essere nascoste da qualche parte (non è necessario che siano nel modulo principale: è chiaro dal loro nome quello che fanno):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}

3
Non sta cercando di spingerlo troppo oltre con YESe NO?
rmobis

@ Raphael_ - potrebbe anche essere: stavo solo mostrando un "per esempio". Molte persone usano vero / falso, ovviamente. Ma non riesco mai a ricordare (perché diversi linguaggi utilizzano convenzioni diverse): è è TRUE, Trueo true? E quali file di intestazione dovrei includere nel normale C, se ce ne sono? Quindi ho rotolato il mio. Chissà se è questo che ha ottenuto un voto negativo ...
Floris

1

Per il primo:

if (x % 10 == 0)

si applicherà a:

10, 20, 30, .. 100 .. 1000 ...

Per il secondo:

if (((x-1) / 10) % 2 == 1)

richiederà:

11-20, 31-40, 51-60, ..

Fondamentalmente prima facciamo x-1per ottenere:

10-19, 30-39, 50-59, ..

Quindi li dividiamo 10per ottenere:

1, 3, 5, ..

Quindi controlliamo se questo risultato è dispari.


1

Puoi provare quanto segue:

        // multiple of 10
        if ((num % 10) == 0)
        {
           // Do something
        }
        else if (((num / 10) % 2) != 0)
        {
            //11-20, 31-40, 51-60, 71-80, 91-100
        }
         else
        {
            //other case
        }

Nella domanda OP il controllo per il multiplo di 10 non è correlato al controllo del range, e nel controllo del range 20 deve essere sullo stesso range di 11, con il vostro codice ((20/10)% 2) -> ( 2% 2) -> 0
Serpiton

0

So che questa domanda ha così tante risposte, ma la getterò comunque qui ...

Tratto da Code Complete di Steve McConnell , seconda edizione: "Tabelle di accesso per gradini:

Un altro tipo di accesso al tavolo è il metodo del gradino. Questo metodo di accesso non è diretto come una struttura di indice, ma non spreca tanto spazio dati. L'idea generale delle strutture a gradini, illustrata nella Figura 18-5, è che le voci in una tabella sono valide per intervalli di dati piuttosto che per punti dati distinti.

Inserisci qui la descrizione dell'immagine

Figura 18-5 L'approccio a gradini classifica ciascuna voce determinando il livello al quale colpisce una "scala". Il "passaggio" che colpisce determina la sua categoria.

Ad esempio, se stai scrivendo un programma di valutazione, l'intervallo di immissione "B" potrebbe essere compreso tra il 75% e il 90%. Ecco una serie di voti che potresti dover programmare un giorno:

Inserisci qui la descrizione dell'immagine

Per utilizzare il metodo del gradino, inserisci l'estremità superiore di ogni intervallo in una tabella e poi scrivi un ciclo per controllare un punteggio rispetto all'estremità superiore di ogni intervallo. Quando trovi il punto in cui il punteggio supera per la prima volta il massimo di un intervallo, sai qual è il voto. Con la tecnica del gradino, devi stare attento a gestire correttamente i punti finali degli intervalli. Ecco il codice in Visual Basic che assegna i voti a un gruppo di studenti in base a questo esempio:

Inserisci qui la descrizione dell'immagine

Sebbene questo sia un semplice esempio, puoi facilmente generalizzarlo per gestire più studenti, più schemi di valutazione (ad esempio, voti diversi per diversi livelli di punteggio su compiti diversi) e modifiche allo schema di valutazione. "

Code Complete , 2nd Edition, pagine 426 - 428 (capitolo 18).


perché pensi che questa sia la domanda sbagliata? solo perché non ho fornito un esempio sul caso OP non significa che ho risposto alla domanda sbagliata ...
lauCosma

0

Come altri hanno sottolineato, rendere le condizioni più concise non velocizzerà la compilazione o l'esecuzione, e non necessariamente aiuta nemmeno con la leggibilità.

Può aiutare a rendere il tuo programma più flessibile, nel caso in cui decidi in seguito di volere la versione del gioco per bambini su una tavola 6 x 6, o una versione avanzata (che puoi giocare tutta la notte) su una tavola 40 x 50 .

Quindi lo codificherei come segue:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Sì, è prolisso, ma chiarisce esattamente cosa sta succedendo sul tabellone.

Se stavo sviluppando questo gioco da visualizzare su un telefono o tablet, creerei variabili ROWS e COLUMNS invece di costanti, in modo che possano essere impostate dinamicamente (all'inizio di un gioco) in modo che corrispondano alle dimensioni e all'orientamento dello schermo.

Consentirei anche di modificare l'orientamento dello schermo in qualsiasi momento, a metà partita: tutto ciò che devi fare è cambiare i valori di RIGHE e COLONNE, lasciando tutto il resto (il numero quadrato corrente su cui si trova ciascun giocatore e il i quadrati di inizio / fine di tutti i serpenti e le scale) invariati. Quindi devi 'solo' disegnare bene la lavagna e scrivere il codice per le tue animazioni (presumo che fosse lo scopo delle tue ifdichiarazioni) ...


Ho anche votato in alto la risposta di Floris - uno stile diverso per ottenere un risultato simile, che non avevo visto prima di scrivere la mia risposta
Laurence Renshaw

2
dovresti usare la funzione inline invece di#define
Bryan Chen

È buona norma, quando si utilizzano #defineistruzioni simili a funzioni , inserire parentesi attorno agli argomenti, dove compaiono nell'espansione. Quindi invece di #define finished(num) (num == lastSquare)scrivere tu #define finished(num) ((num) == lastSquare). Il motivo è che se usi un'istruzione di questo tipo con un'espressione contenente un operatore con una precedenza sufficientemente bassa, non otterrai la risposta che ti aspetti. In questo caso, se non usi le parentesi extra, allora si finished(a & b)espande in (a & b == lastSquare)cui quasi certamente non è quello che vuoi.
Dawood ibn Kareem
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.