简体   繁体   中英

How do I use modulo arithmetic to implement caesar cipher

I am trying to perform a wrap around of the ASCII alphabet characters in order to perform a shift from a key. For example, if the key is 2 then shift the letter A to become C . But, how do I wrap around Z in order to get to B using modulus arithmetic?

I am trying to implement the following formula:

ci = (pi + key) % 26;

Where ci is the i th ciphered letter and pi is the i th letter to be ciphered.

I believe you need to work with "relative" values, rather than absolute values.

Use something like

ci = ((pi - 'A' + key) % 26 ) + 'A';

Character integer constants stores the encoded values, in this case, ASCII . Here, 'A' starts from an offset (decimal value of 65), not from 0. So, before you can wrap the result using a % 26 operation, you need to get that offset out. Once the calculation is done, add the offset back to get the proper ASCII representation.

Others have already shown the basic concept of subtracting the offset of ASCII letters, but you have to be careful in when crossing from capital to non capital letters, as there are some symbols in between which I guess you want to avoid. I have added some logic that enables negative shifts and keeps the letter within capital letter or non capital letter space and tests whether it is a letter at all.

#include <stdio.h>

#define ASCII_CAP_LETTER_OFFS 65
#define ASCII_LETTER_OFFS 97
#define NUM_OF_LETTERS 26

char shift_letter (char letter, short shift)
{
  char ci;
  short shift_lcl = shift % NUM_OF_LETTERS;
  if (shift_lcl >= 0)
  { // shift in positive direction
  }
  else
  { // shift in negative direction 
   shift_lcl = NUM_OF_LETTERS + shift_lcl;
  }

  if (letter >= ASCII_CAP_LETTER_OFFS && letter < ASCII_CAP_LETTER_OFFS + NUM_OF_LETTERS)  
    {// its a capital letter
      ci =
    (letter + shift_lcl - ASCII_CAP_LETTER_OFFS) % NUM_OF_LETTERS +
    ASCII_CAP_LETTER_OFFS;
    }
  else if (letter >= ASCII_LETTER_OFFS && letter < ASCII_LETTER_OFFS + NUM_OF_LETTERS) 
    {// its a non capital letter
      ci =
    (letter + shift_lcl - ASCII_LETTER_OFFS) % NUM_OF_LETTERS +
    ASCII_LETTER_OFFS;
    }
  else
    {
      printf ("This was not a letter!\n");
      ci = 0;
    }
  return ci;
}

int main ()
{
  char test_letter = 'a';
  short test_shift = -53;
  char shifted_letter = 0;
  shifted_letter = shift_letter (test_letter, test_shift);
  printf("%c +  %d = %c", test_letter, test_shift, shifted_letter);
}

Deeper

 ci = ((pi - 'A' + key) % 26) + 'A';

as well answered by @Sourav Ghosh generates the expected encoded AZ when key is non-negative and not too large. It reduces pi with - 'A' so the value is in the [0...25] range and the re-offsets it after the % calculation.

To work for the full int range of key takes a little more code.

  • Reduce key range [INT_MIN...INT_MAX] with the functional equivalent range of [-25 ... 25] with key % 26 . This step is important to prevent int overflow with pi - 'A' + key . This can be done once if a number of letters need encoding.

  • Add 26. This insures negative keys are moved to positive ones.

     ci = ((pi - 'A' + key%26 + 26) % 26 ) + 'A'; 

Note: In C, % is not the mod operator, but the remainder operator. Functional differences occur with a%b when a and/or b are negative. ref

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM