Reputation: 11
Help i'm trying to descramble a file using a permutation as a key,i know how to scramble it but i need to create a function to descramble it back to what it was using the same permutation key, here's the code:
def stringtoarray(S):
A = []
i = 0
while i<len(S):
A.append(S[i])
i += 1
return A
def arraytostring(A):
S = ""
for c in A: # "for each element/value c in array A"
S = S+c
return S
#arraytostring
def scramble(S, P): #Scramble string S with permutation P
pn = len(P) # length of permutation array
E = (len(S) + pn - (len(S)%pn)) * [' '] # array of chars, padded
i = 0
while i< len(S):
seg = i/pn # segment number
j = i % pn # segment offset
E[ seg*pn + P[j] ] = S[i]
i += 1
# while
return arraytostring(E)
# scramble
print scramble("0123456789abcdefghij",[9, 14, 11, 19, 16, 18, 12, 6,
7, 15, 0, 5, 17, 4, 3, 10, 2, 1, 8, 13])
# prints ahgedb78i0f26j194c53
I want to set it back to string "0123456789abcdefghij" again
Upvotes: 1
Views: 2185
Reputation: 50557
Firstly some comments on your code.
The following is completely unnecessary:
def stringtoarray(S):
A = []
i = 0
while i<len(S):
A.append(S[i])
i += 1
return A
It can be achieved by simply doing list(your_string)
Also, if you want to iterate over something, you can use for .. in ..
eg.
for char in some_string:
# do something with character
Your arraytostring
function is also unnecessary. The proper way to concatenate a list into a string is to use string.join()
[docs]. For example, ''.join(['a', 'b', 'c'])
would give you 'abc'
.
This is how I would do the scramble/unscramble functions:
def scramble(s, p):
chars = list(s)
scrambled = ['']*len(chars)
for char, i in zip(chars, p):
scrambled[i] = char
return ''.join(scrambled)
def unscramble(s, p):
reverse_p = sorted(range(len(p)), key=p.__getitem__)
return scramble(s, reverse_p)
s = "abcdefg"
p_key = [6, 1, 3, 0, 5, 2, 4]
print "original:", s
scrambled = scramble(s, p_key)
print "scrambled:", scrambled
print "unscrambled:", unscramble(scrambled, p_key)
Result:
original: 0123456789abcdefghij scrambled: ahgedb78i0f26j194c53 unscrambled: 0123456789abcdefghij
Upvotes: 0
Reputation: 880587
You have, in a sense, already written the de-scrambler. All you have to do is feed scrambler
the inverse permutation. Below, the inverse permutation is given by argsort(P)
where P
is the permutation.
def scramble(S, P): #Scramble string S with permutation P
pn = len(P) # length of permutation array
E = (len(S) + pn - (len(S)%pn)) * [' '] # array of chars, padded
for i in range(len(S)):
seg,j = divmod(i,pn)
E[ seg*pn + P[j] ] = S[i]
return ''.join(E).rstrip()
def argsort(seq):
# http://stackoverflow.com/questions/3382352/3382369#3382369
'''
>>> seq=[1,3,0,4,2]
>>> index=argsort(seq)
[2, 0, 4, 1, 3]
Given seq and the index, you can construct the sorted seq:
>>> sorted_seq=[seq[x] for x in index]
>>> assert sorted_seq == sorted(seq)
Given the sorted seq and the index, you can reconstruct seq:
>>> assert [sorted_seq[x] for x in argsort(index)] == seq
'''
return sorted(range(len(seq)), key=seq.__getitem__)
P=[9, 14, 11, 19, 16, 18, 12, 6, 7, 15, 0, 5, 17, 4, 3, 10, 2, 1, 8, 13]
text="0123456789abcdefghij"
print scramble(text,P)
# ahgedb78i0f26j194c53
print(scramble(scramble(text,P),argsort(P)))
# 0123456789abcdefghij
By the way, instead of pre-allocating enough space for E
with
E = (len(S) + pn - (len(S)%pn)) * [' ']
you can generate the items in E
in order by using argsort
:
def scramble(S, P):
pn = len(P)
E = []
idx = argsort(P)
for i in range(len(S)):
seg,j = divmod(i,pn)
E.append(S[idx[j]+seg*pn])
return ''.join(E)
Upvotes: 3
Reputation: 604
Easy. Compute the inverse of the permutation and apply that. There's a well-known algorithm for this; you can find it cited as Algorithm J from Donald Knuth's "The Art Of Computer Programming".
There appears to be source code here: http://binetacm.wikidot.com/algo:perminv
void inversePermutation(int perm[], int n, int inv[]) {
for (int i=0; i<n; i++)
inv[perm[i]]=i;
}
Upvotes: 0