User:Mwn3d/Seasoning Sandwich Caesar Cipher: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Updated to use arrays instead of Maps. No idea what I was thinking.)
(This is probably getting way too crazy...add code to make the noise look more like the actual message with word length restrictions)
Line 25: Line 25:
StringBuilder encoded = new StringBuilder();
StringBuilder encoded = new StringBuilder();
if(addNoise){ //if we are encoding
if(addNoise){ //if we are encoding
StringBuilder noise = genNoise(enc.replaceAll("\\W", "")); //get noise based on the original string ignoring non-letter characters
StringBuilder noise = genNoise(enc); //get noise based on the original string ignoring non-letter characters
noise.insert((int)(Math.random() * noise.length()), " " + enc + " "); //insert the message into a random place in the noise
noise.insert((int)(Math.random() * noise.length()), " " + enc + " "); //insert the message into a random place in the noise
Line 46: Line 46:
* Generate noise to surround the actual message
* Generate noise to surround the actual message
*/
*/
private static StringBuilder genNoise(String enc){
private static StringBuilder genNoise(String orig){
//remove all non-word chars for figuring out which letters to include in noise
String enc = orig.replaceAll("\\W", "");
//get the number of each letter needed to bring them all up to the same frequency
//get the number of each letter needed to bring them all up to the same frequency
int[] invertedFreq = invert(letterFreq(enc));
int[] invertedFreq = invert(letterFreq(enc));
Line 60: Line 63:
StringBuilder noise = new StringBuilder();
StringBuilder noise = new StringBuilder();
Random rand = new Random();
Random rand = new Random();
double meanLen = getMeanLen(orig.split("\\b++"));
int waitForSpace = rand.nextInt(rand.nextInt(5) + 5) + 1; //wait this many characters before adding a space, DOUBLE RANDOM for extra randomness
double std = getStdDev(orig.split("\\b++"));
int waitForSpace = (int)(rand.nextGaussian() * std + meanLen) + 1; //wait this many characters before adding a space
while(rawNoise.length() > 0){ //while we still have letters left to add
while(rawNoise.length() > 0){ //while we still have letters left to add
Line 71: Line 77:
if(waitForSpace == 0){ //if we hit the wait time
if(waitForSpace == 0){ //if we hit the wait time
noise.append(' '); //add a space
noise.append(' '); //add a space
do{
waitForSpace = rand.nextInt(rand.nextInt(5) + 5) + 1; //select a new wait time
waitForSpace = (int)(rand.nextGaussian() * std + meanLen) + 1; //select a new wait time
}while(waitForSpace == 0);
}
}
}
}
Line 106: Line 114:
return freqLet;
return freqLet;
}
private static double getMeanLen(String[] words){
int total = 0;
for(String word:words){
total += word.length();
}
return (double)total / words.length;
}
private static double getStdDev(String[] words){
double mean = getMeanLen(words);
int totalDiff = 0;
for(String word:words){
totalDiff += (int)Math.pow(word.length() - mean, 2);
}
return Math.sqrt(totalDiff / words.length);
}
}
}</lang>
}</lang>
{{out}}
{{out}}
<pre>The quick brown fox Jumped over the lazy Dog and then he landed on a cat and it hurt
<pre>The quick brown fox Jumped over the lazy Dog and then he landed on a cat and it hurt
y rv gnnj ybxwi eej liu s gdnih vrvbxu z c c lkh khykw ftq cguow ndaiz raj vgybqp ahqd ftq xmlk pas mzp ftqz tq xmzpqp az m omf mzp uf tgdf rteeuh isyckaj ei nnbs lxwjcx vlas kdico bhs dgbe suf rouv woldrl nokhct pjyw mex bygvor w j
tsl wu ebjc usjx ab ld xuj tx jos g rl nj ne khnh hg ny fsr su yvik kj wds vvw bxyu xwld lhn mrrl wdv wocvr h gep oeb bzyi ftq cguow ndaiz raj vgybqp ahqd ftq xmlk pas mzp ftqz tq xmzpqp az m omf mzp uf tgdf ve ci cci ekh ykcbi ar oiy ek ong
m fj ubbx mplkw ssx zwi g urbwv jfjpli n q q zyv yvmyk the quick brown fox jumped over the lazy dog and then he landed on a cat and it hurt fhssiv wgmqyox sw bbpg zlkxql jzog yrwqc pvg rups git fcij kczrfz bcyvqh dxmk asl pmujcf k x</pre>
hgz ki spxq igxl op zr lix hl xcg u fz bx bs yvbv vu bm tgf gi mjwy yx krg jjk plmi lkzr zvb affz krj kcqjf v usd csp pnmw the quick brown fox jumped over the lazy dog and then he landed on a cat and it hurt js qw qqw syv myqpw of cwm sy cbu</pre>

Revision as of 18:43, 20 October 2014

I was reading about the Caesar cipher and it was mentioned that an easy way for a person to decode the message would be to find the most common character and consider it to be "E" (or the most common character in whatever language). Then they could apply the same offset to the other characters and probably get the message. I wanted to make a version that would make that strategy impossible (don't worry, computers make the Caesar cipher really easy to beat no matter what you do). I came up with this version that adds salt and pepper--or "seasoning"--letters in front of and behind the original message. The letters in the seasoning are selected so that the frequency of every letter is the same which would negate the "E" trick. the seasoning also has spaces so that it looks sort of natural. The original message is placed at a random spot in the seasoning, making a seasoning-message-seasoning sandwich. When the message is decoded on the other end the recipient just has to scan until it runs into readable text to find the message. <lang java5>import static java.util.Collections.nCopies;

import java.util.Random;

public class Cipher{ public static void main(String[] args){ String original = "The quick brown fox Jumped over the lazy Dog and then he landed on a cat and it hurt"; String enc = encode(original, 12); System.out.println(original); System.out.println(enc); System.out.println(decode(enc, 12)); }

public static String decode(String enc, int offset){ return encode(enc, -offset, false); }

public static String encode(String enc, int offset){ return encode(enc, offset, true); }

private static String encode(String enc, int offset, boolean addNoise){ offset = offset % 26 + 26; //make the offset a valid number StringBuilder encoded = new StringBuilder(); if(addNoise){ //if we are encoding StringBuilder noise = genNoise(enc); //get noise based on the original string ignoring non-letter characters

noise.insert((int)(Math.random() * noise.length()), " " + enc + " "); //insert the message into a random place in the noise enc = noise.toString().replaceAll("[ ]{2,}"," ").trim(); //get rid of double spaces and spaces at the start and end }

//do the regular encoding using the original string with noise around it for(char i : enc.toLowerCase().toCharArray()){ if(Character.isLetter(i)){ int j = (i - 'a' + offset) % 26; encoded.append((char)(j + 'a')); }else{ encoded.append(i); } } return encoded.toString(); }

/* * Generate noise to surround the actual message */ private static StringBuilder genNoise(String orig){ //remove all non-word chars for figuring out which letters to include in noise String enc = orig.replaceAll("\\W", "");

//get the number of each letter needed to bring them all up to the same frequency int[] invertedFreq = invert(letterFreq(enc));

StringBuilder rawNoise = new StringBuilder();

//start with all of those letters in organized groups for(int i = 0; i < invertedFreq.length; i++){ //string multiplication would be nice here rawNoise.append(nCopies(invertedFreq[i], (char)('a' + i)).toString().replaceAll("\\W", "")); }

StringBuilder noise = new StringBuilder(); Random rand = new Random(); double meanLen = getMeanLen(orig.split("\\b++")); double std = getStdDev(orig.split("\\b++"));

int waitForSpace = (int)(rand.nextGaussian() * std + meanLen) + 1; //wait this many characters before adding a space

while(rawNoise.length() > 0){ //while we still have letters left to add int randIdx = rand.nextInt(rawNoise.length()); //pick a random letter char ch = rawNoise.charAt(randIdx); if(noise.length() >= 3 && noise.substring(noise.length() - 3).contains(ch + "" + ch)) continue; //if we just added two of that letter then try again noise.append(ch); //add that letter rawNoise.deleteCharAt(randIdx); //remove it from our list of letters to add waitForSpace--; //we added another character so we decrease the wait time for spaces if(waitForSpace == 0){ //if we hit the wait time noise.append(' '); //add a space do{ waitForSpace = (int)(rand.nextGaussian() * std + meanLen) + 1; //select a new wait time }while(waitForSpace == 0); } }

return noise; }

/* * Count up the frequency of letters in a given string (case-insensitive, ignores non-letter chars) */ private static int[] letterFreq(String msg){ int[] freq = new int[26];

for(char ch : msg.toLowerCase().toCharArray()){ freq[ch - 'a']++; }

return freq; }

/* * Calculate the number of each character needed to bring each one up to the same frequency */ private static int[] invert(int[] arr){ int max = 0; for(int i:arr){ if(i > max) max = i; }

int[] freqLet = new int[arr.length]; for(int i = 0; i < arr.length; i++){ freqLet[i] = max - arr[i]; }

return freqLet; }

private static double getMeanLen(String[] words){ int total = 0; for(String word:words){ total += word.length(); } return (double)total / words.length; }

private static double getStdDev(String[] words){ double mean = getMeanLen(words); int totalDiff = 0; for(String word:words){ totalDiff += (int)Math.pow(word.length() - mean, 2); } return Math.sqrt(totalDiff / words.length); } }</lang>

Output:
The quick brown fox Jumped over the lazy Dog and then he landed on a cat and it hurt
tsl wu ebjc usjx ab ld xuj tx jos g rl nj ne khnh hg ny fsr su yvik kj wds vvw bxyu xwld lhn mrrl wdv wocvr h gep oeb bzyi ftq cguow ndaiz raj vgybqp ahqd ftq xmlk pas mzp ftqz tq xmzpqp az m omf mzp uf tgdf ve ci cci ekh ykcbi ar oiy ek ong
hgz ki spxq igxl op zr lix hl xcg u fz bx bs yvbv vu bm tgf gi mjwy yx krg jjk plmi lkzr zvb affz krj kcqjf v usd csp pnmw the quick brown fox jumped over the lazy dog and then he landed on a cat and it hurt js qw qqw syv myqpw of cwm sy cbu