Below, I have created a rudimentary script to decipher a columnar transposition. I am wondering if there is a more efficient way to determine the number of the rows in a column? Also, is there a way to build the resulting string without creating a matrix in the interim?
Overview
Quick refresher for columnar transpositions.
Method 1
Given a key of 3
,
the text 'Attack at dawn'
is transposed into three columns...
1 2 3
A t t
a c k
a t D
a w n
...joining each column; left->right, top->bottom; will result in the following text:
Aaaa tctw tkDn
Method 2
Given a key 'table'
,
the text 'Attack at dawn'
is transposed into five columns (length of the key)...
t a b l e
A t t a c
k a t D a
w n
...joining the columns, as described above, result in the following text:
tAkw atan btt laD eca
Code
Is there a better way to determine how many rows will be present for each column?
function decrypt(ciphertext, key) {
var chars = normalizeText(ciphertext);
var len = chars.length;
var isStr = typeof key === 'string';
var cols = isStr ? key.length : key;
var rows = Math.ceil(len / cols);
var mod = divMod(len, cols)[1];
var matrix = Matrix(rows, cols, '');
var counter = 0;
for (var col = 0; col < cols; col++) {
var remainder = mod !== 0 && col >= mod ? 1 : 0;
for (var row = 0; row < rows - remainder; row++) {
matrix[row][col] = chars[counter++];
}
}
return (function(plaintext, startIndex) {
return plaintext.substr(startIndex);
}(matrix.reduce(function(result, row) {
return result + row.join('');
}, ''), isStr ? cols : 0));
}
Demo
[ [ 'Aaaa tctw tkDn' , 3 ],
[ 'tAkw atan btt laD eca', 'table' ]
].forEach(function(cipher) {
println(decrypt(cipher[0], cipher[1]));
});
function decrypt(ciphertext, key) {
var chars = normalizeText(ciphertext);
var len = chars.length;
var isStr = typeof key === 'string';
var cols = isStr ? key.length : key;
var rows = Math.ceil(len / cols);
var mod = divMod(len, cols)[1];
var matrix = Matrix(rows, cols, '');
var counter = 0;
for (var col = 0; col < cols; col++) {
var remainder = mod !== 0 && col >= mod ? 1 : 0;
for (var row = 0; row < rows - remainder; row++) {
matrix[row][col] = chars[counter++];
}
}
return (function(plaintext, startIndex) {
return plaintext.substr(startIndex);
}(matrix.reduce(function(result, row) {
return result + row.join('');
}, ''), isStr ? cols : 0));
}
function divMod(y, x) {
return [~~(y / x), y % x];
}
function normalizeText(ciphertext) {
return ciphertext.replace(/ +/g, "").split('');
}
function Matrix(rows, cols, defaultVal) {
defaultVal = defaultVal !== undefined ? defaultVal : 0;
var arr = [];
for (var i = 0; i < rows; i++) {
arr.push([]);
arr[i].push(new Array(cols));
for (var j = 0; j < cols; j++) {
arr[i][j] = defaultVal;
}
}
return arr;
}
function print(str, targetEl) {
return (function (el) {
return el.html(el.html() + (str || ''));
}(targetEl || $('#out')));
}
function println(str, targetEl) {
print((str || '') + '<br />', targetEl)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="out"></div>
3 Answers 3
Allow me to provide a bit of a different approach. I know it does not exactly answer your questions, but I believe it makes them somewhat obsolete.
This is how I would do the decrypt:
function decrypt(sentence) {
var result = '';
var pos = 0;
var words = sentence.split(' ');
words = words.map(function(word) {
return word.split('');
});
while(true) {
char = words[pos].shift();
if (! char) {
break;
}
result += char;
pos = pos > words.length -2 ? 0 : pos + 1;
}
return result;
}
For a working example: http://jsfiddle.net/35jyyv8g/1/
Note that I left the key out, since it is not really necessary with the spaces in the input.
For me, all it takes are two for loops, the first one with i going from 0 to cols (stricly inferior), with increments of 1, and the second loop (inside the first one) with j going from i to len (stricly inferior) with increments of rows . And the characters you want will be at the position j of the initial message. Of course, a better naming for i and j would be a good thing.
Interesting question,
first off, your encryption approach has some serious problems:
To know the encryption key length, you just need
ciphertext.split(' ').length
Worse, the cipher-text even has the key in clear text:
tAkw atan btt laD eca
This means @Pevara's approach is correct, and far more elegant. (It does also output the key for strings encrypted with the second method.)
My personal approach uses a bit of ES6 and is a bit more functional:
function simpleDecrypt2(cipher) {
let out = '',
parts = cipher.split(' ').map(function(part) { return part.split(''); });
while( parts[0].length ){
out += parts.reduce( (out,part)=>out+(part.shift()||''), '' );
}
return out;
}
As for your first question, you could have calculated the row count by
ciphertext.split(' ')[0].length