Rail fence cipher- Is there any better solution?

I have coded Rail Fence Cipher in Python. I was wondering if there could be a better solution.

For those who don't know what rail fence cipher is, it is basically a method of writing plain text in a way it creates linear pattern in a spiral way. Example - when "FOOBARBAZ" rail-fenced using key of 3.

``````F . . . A . . . Z
O . B . R . A . Q . X
O . . . B . . . U
``````

Reading the above spiral line-by-line, the cipher text becomes "FAZOBRAQXOBU". Read more at - Rail fence - Wikipedia.

``````def cipher(s, key, graph=False) :
down=True
raw_out=[]
out=''
i=0
for x in range(key) :
raw_out.append({})
for pos in range(len(s)) :
raw_out[i][pos]=s[pos]
if i==key-1 :
down=False
if i==0 :
down=True
if down :
i=i+1
else :
i=i-1
for p in raw_out :
for q in p :
out+=p[q]
if graph :
return raw_out
return out

def decipher(s, key) :
map_list=cipher(s, key, True) #CREATING JUST FOR MAPPING - WHICHth CHARACTER OF THE STRING - IS WHICHth CHARACTER OF THE CIPHER
new={}
out=''
s_counter=0
for x in map_list :
for y in x :
new[y]=s[s_counter]
s_counter+=1
for p in new :
out+=new[p]
return map_list
``````

I was wondering if there was any better way of doing this, since my procedure is very costly, it a uses couple of dictionaries.

Code in any language is welcomed.

Winning criterion - Shorter code than mine and without using dictionaries.

-
What is the winning criterion ? –  Paul R Jan 25 at 10:37

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 (`_`), and then that character is stripped off if necessary it is converted to a list, and padded with an empty string.

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←⍞]
``````
-