Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

How can the Caesar cipher be implemented in Java?

+2
−1

The Caesar cipher is a substitution cipher in which the letters of an ordered alphabet are cyclically shifted by a given number. A detailed description can be found e.g. here.

Let u be the index of the letter to be encrypted in the alphabet, shift the shift and size the number of letters of the alphabet, then the following applies to the index v of the encrypted letter:

v = mod(u + shift, size)

and for decryption:

u = mod(v - shift, size)

How can this algorithm be implemented in Java?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

2 answers

+2
−0

Caesar Cipher originally deals only with letters (ASCII "A" to "Z", without diacritics/accents), so I'm not sure why you included ۤ$%& in your answer. And using indexOf (as you did) is not very efficient, because it always makes a loop through the string, until the character is found (which means you're looping the alphabet string lots of times, which might not be a good idea).

Of course for small strings this won't make much difference, but anyway, to implement this cipher (shifting only ASCII letters) you don't need a string with the whole alphabet. Just use the char's ASCII code and do some calculations with it. To eliminate the problem of the % operator with negative numbers, use Math.floorMod, which guarantees that the result is positive (actually, Math.floorMod(x, y) guarantees that the result has the same sign of y, and we're going to use y equals 26, so this will do the trick)[1]:

public static String caesarCipher(String s, int shift) {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        char base;
        // check if it's an ASCII letter
        if ('A' <= c && c <= 'Z') {
            base = 'A';
        } else if ('a' <= c && c <= 'z') {
            base = 'a';
        } else {
            // not a letter, char remains the same in the new string
            result.append(c);
            continue; // go to next char
        }
        // it only gets here if it's an ASCII letter
        result.append((char) (Math.floorMod((c - base) + shift, 26) + base));
    }
    return result.toString();
}

First I check if the character to be shifted is a lowercase or uppercase ASCII letter, and set the base letter accordingly (for non-letters, I'll just add them to the result without any modification, but you can also throw an exception if those are not to be accepted). Then I "normalize" the character relative to the base letter (so "A"/"a" becomes zero, "B"/"b" becomes 1 and so on). Then I add the shift and get the floorMod of that by 26.

After that, I'll just add the base again, so 0 becomes "A" (or "a", if the base is lowercase), 1 becomes "B"/"b", etc.

Testing:

String s = "aA bB yY zZ 123";
String ciphered = caesarCipher(s, 3);
System.out.println(ciphered);
String deciphered = caesarCipher(ciphered, -3);
System.out.println(deciphered);

Output:

dD eE bB cC 123
aA bB yY zZ 123

As I said, I'm ignoring (keeping unchanged) all the characters that aren't letters, but you can also throw an exception if those are not allowed in the input string. The description in Wikipedia doesn't say what to do with non-letters, but in the examples we can see that at least spaces are allowed (and kept unchanged). You could include those specific checks if you want, but I guess that's beside the point and left as an exercise for the reader.


"Generic" alphabet

If you want a more generic alphabet (as you did in your answer, by including ۤ$%&), the calculations won't work anymore, and the indexOf approach could be used instead.

But if this will be used in lots of strings, indexOf won't be very efficient (as already said above), and perhaps it'd be worth to do some pre-processing in the alphabet:

public class CaesarCipher {
    private int encriptionShift;
    private String alphabet;
    private Map<Character, Integer> positions; // map each char to its position in the alphabet

    // receives the alphabet and the shift for encription
    public CaesarCipher(String alphabet, int encriptionShift) {
        this.encriptionShift = encriptionShift;
        this.alphabet = alphabet;
        this.positions = new HashMap<>(alphabet.length());
        for (int i = 0; i < alphabet.length(); i++) {
            this.positions.put(alphabet.charAt(i), i);
        }
    }

    private String shiftString(String s, int shift) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (this.positions.containsKey(c)) {
                int pos = this.positions.get(c);
                int newPosition = Math.floorMod(pos + shift, this.alphabet.length());
                result.append(this.alphabet.charAt(newPosition));
            } else {
                // char not in the alphabet, keeping unchanged
                // (or throw exception if you don't want to allow those)
                result.append(c);
            }
        }
        return result.toString();
    }

    public String cipher(String s) {
        return this.shiftString(s, this.encriptionShift);
    }

    public String decipher(String s) {
        return this.shiftString(s, -this.encriptionShift);
    }
}

Now each character is mapped to its position in the alphabet (only once, when the instance is created), and I use this map instead of indexOf (it's a classic trade-off: the map uses extra memory, but I avoid all the loops caused by indexOf). Of course you need to analyze if performance is an issue and if it's worth doing it, but anyway...

Testing:

String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzۤ$%&";
CaesarCipher c = new CaesarCipher(alphabet, 3);

String plaintext = "ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
String ciphertext = c.cipher(plaintext);
String decrypted = c.decipher(ciphertext);
System.out.println(plaintext);
System.out.println(ciphertext);
System.out.println(decrypted);

Output:

ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
%&ABC3456789abcdefghijklmnopqrstuvwxyzۤ$DEFGHIJKLMNOPQRSTUVWXYZ012
ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

  1. Of course you could also use your mod function, I'm just showing a native "batteries included" solution. ↩︎

History
Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

+1
−0

The formulas for encryption and decryption require a positive value of the modulo operation. However, this is not guaranteed for all implementations of the modulo operator. For instance, in Python the modulo value is always positive (e.g. -2 % 5 = 3), while in Java it can be negative (e.g. -2 % 5 = -2).

The following Java implementation reliably generates a positive value:

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

Since encryption and decryption differ only in the sign of shift, encryption and decryption can be implemented together. A possible implementation for e.g. the alphabet ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzۤ$%& could be:

private static String encryptDecrypt(String text, int shift) {
	
    String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzۤ$%&";
    int size = alphabet.length(); 
	
    StringBuffer result = new StringBuffer();
    for (int i = 0; i < text.length(); i++) {
        int u = alphabet.indexOf(text.charAt(i));
        if (u == -1) {
            throw new RuntimeException(text.charAt(i) + " not allowed...");
        }
        int v = mod(u + shift, size);
        result.append(alphabet.charAt(v));
    }

    return result.toString();
}

Test:

String plaintext = "ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
String ciphertext = encryptDecrypt(plaintext, 3); 	// Encryption: right shift
String decrypted = encryptDecrypt(ciphertext, -3);	// Decryption: left shift
System.out.println(plaintext);		// ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
System.out.println(ciphertext);		// %&ABC3456789abcdefghijklmnopqrstuvwxyzۤ$DEFGHIJKLMNOPQRSTUVWXYZ012
System.out.println(decrypted);		// ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ	
History
Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »