I have a list of possible passwords, and I need to append simple transformations of each password in this list. Say my list is
['sauce', 'banana']
and I have a series of transformations shown here.
'a'-->'@'
's'-->'$'
I then want to add to the list every possible transformation. So now the list should look something like
['$auce', 's@uce', '$@uce', 'b@nana', 'ban@na',
'banan@', 'b@n@na', 'b@nan@,' 'ban@n@', 'b@n@n@']
How would I do that in Python?
I tried first creating a function that made all transformations. Then I took that transformed string and essentially did a cross product with the original string. However, this causes a lot of repeats, and it seems a bit hacky.
The function:
def symbolize(s):
options = {
'a': '@',
'S': '$'
}
copy = ''
for i in range(len(s)):
if s[i] in options:
copy += options[s[i]]
else:
copy += s[i]
return copy
And then the cross product:
for x in range(len(candidates)):
candidates += list(''.join(t) for t in itertools.product(
*zip(candidates[x], symbolize(candidates[x]))))
from itertools import product
def all_versions_of_word(word, alt_chars, skip_orig=True):
chars = [ch + alt_chars.get(ch, "") for ch in word]
combos = product(*chars)
if skip_orig and word: next(combos) # drop the first item
return ("".join(c) for c in combos)
def transform_passwords(passwords, alt_chars={"a":"@", "s":"$"}):
for word in passwords:
yield from all_versions_of_word(word, alt_chars)
which runs like
>>> list(transform_passwords(['sauce', 'banana']))
['s@uce',
'$auce',
'$@uce',
'banan@',
'ban@na',
'ban@n@',
'b@nana',
'b@nan@',
'b@n@na',
'b@n@n@']
If you want you can use recursion, although pytohn limits it to a depth of 2000:
Create a mapping and the list:
lst = ['sauce', 'banana']
mapping = {'a':'@', 's':'$'}
Now recursively generate all possibilities (including no replacement at all):
def opts(_mapping, _str):
if not _str:
yield ""
else:
for opt in opts(_mapping, _str[1:]):
if _str[0] in _mapping:
yield _mapping[_str[0]] + opt
yield _str[0] + opt
Output:
[list(opts(mapping, st)) for st in lst]
=> [['$@uce', 's@uce', '$auce', 'sauce'], ['b@n@n@', 'ban@n@', 'b@nan@', 'banan@', 'b@n@na', 'ban@na', 'b@nana', 'banana']]
I really had fun digging into this answer! There's a lot of iterating over the string here, but I like my answer!
import functools
def transform(pwd, subs):
result = {pwd}
stack = [pwd]
# contains all resolved strings left to permute on
while True:
pwd = stack.pop()
# grab a password
if not stack and not any(i in subs for i in pwd):
return result
# if the stack is empty and is no way to permute further,
# then return our result.
for idx,ch in enumerate(pwd):
if ch in subs:
repl = subs[ch]
transformation = pwd[:idx]+repl+pwd[idx+1:]
# transformation is our transformed word
result.add(transformation)
# add our transformation to our result set
stack.append(transformation)
# and toss it on our stack to transform further
def transform_multiple(pwds, subs):
return functools.reduce(set.union,
(transform(pwd, subs) for pwd in pwds))
DEMO:
In [55]: transform_multiple(['banana', 'sauce','ananas'], {'a':'@', 's':'$'})
Out[55]:
{'$@uce',
'$auce',
'@n@n@$',
'@n@n@s',
'@n@na$',
'@n@nas',
'@nan@$',
'@nan@s',
'@nana$',
'@nanas',
'an@n@$',
'an@n@s',
'an@na$',
'an@nas',
'anan@$',
'anan@s',
'anana$',
'ananas',
'b@n@n@',
'b@n@na',
'b@nan@',
'b@nana',
'ban@n@',
'ban@na',
'banan@',
'banana',
's@uce',
'sauce'}
If I were to spend a little more time on this, I'd probably remove the if not stack and not any(...)
call and put a flag inside the for idx,ch in enumerate
loop that flags if there's a change made to that string, then test if not flag and not stack: return result
after the loop ends. This would save us a whole iteration on pwd
and len(pwd)
membership tests every time through the loop.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.