Rail fence cipher
Python 133 bytes
def cipher(t,r):
m=r*2-2;o='';j=o.join
for i in range(r):s=t[i::m];o+=i%~-r and j(map(j,zip(s,list(t[m-i::m])+[''])))or s
return o
Sample usage:
>>> print cipher('FOOBARBAZQUX', 3)
FAZOBRAQXOBU
>>> print cipher('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 4)
AGMSYBFHLNRTXZCEIKOQUWDJPV
>>> print cipher('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 5)
AIQYBHJPRXZCGKOSWDFLNTVEMU
>>> print cipher('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 6)
AKUBJLTVCIMSWDHNRXEGOQYFPZ
Note: the results from even rail counts are different than for the code you provided, but they seem to be correct. For example, 6 rails:
A K U
B J L T V
C I M S W
D H N R X
E G O Q Y
F P Z
corresponds to AKUBJLTVCIMSWDHNRXEGOQYFPZ
, and not AKUTBLVJICMSWXRDNHQYEOGZFP
as your code produces.
The basic idea is that each rail can be found directly by taking string slices [i::m]
, where i
is the rail number (0
-indexed), and m
is (num_rails - 1)*2
. The inner rails additionally need to be interwoven with [m-i::m]
, achieved by zipping and joining the two sets of characters. Because the second of these can potentially be one character shorter, it is padded with a character assumed not to appear anywhere ( it is converted to a list, and padded with an empty string._
), and then that character is stripped off if necessary
A slightly more human readable form:
def cipher(text, rails):
m = (rails - 1) * 2
out = ''
for i in range(rails):
if i % (rails - 1) == 0:
# outer rail
out += text[i::m]
else:
# inner rail
char_pairs = zip(text[i::m], list(text[m-i::m]) + [''])
out += ''.join(map(''.join, char_pairs))
return out
APL 52 41
i←⍞⋄n←⍎⍞⋄(,((⍴i)⍴(⌽⍳n),1↓¯1↓⍳n)⊖(n,⍴i)⍴(n×⍴i)↑i)~' '
If the input text string i and the key number n are preinitialised the solution can be shortened by 9 characters. Running the solution against the examples given by primo gives identical answers:
FOOBARBAZQUX
3
FAZOBRAQXOBU
ABCDEFGHIJKLMNOPQRSTUVWXYZ
4
AGMSYBFHLNRTXZCEIKOQUWDJPV
ABCDEFGHIJKLMNOPQRSTUVWXYZ
5
AIQYBHJPRXZCGKOSWDFLNTVEMU
ABCDEFGHIJKLMNOPQRSTUVWXYZ
6
AKUBJLTVCIMSWDHNRXEGOQYFPZ
On further reflection there appears to be a shorter index based solution:
i[⍋+\1,(y-1)⍴((n←⍎⍞)-1)/1 ¯1×1 ¯1+y←⍴i←⍞]