Iterate through letters of an unknown length of digits - python

Okay... I need to iterate through strings with arbitrary lengths. Since I don't know how to explain this too well, I mean like this:
def zip(string1,string1):
...
and when called with "a" and "ad" it would return a list:
>>>zip("a","ad")
["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","aa","ab","ac","ad"]
I've tried using map(chr,range(ord('a'),ord('nb')+1)) but I get TypeError: ord() expected a character, but string of length 2 found, and I don't know where to go from here. Any ideas?

That's it:
def zip_(start, end):
def __inc(s):
if not s:
return "a"
elif s[-1] != "z":
return s[:-1] + chr(ord(s[-1]) + 1)
else:
return __inc(s[:-1]) + "a"
s = start
yield s
while s != end:
s = __inc(s)
yield s
print list(zip_("a", "ad"))
A few comments:
Don't use the word zip as a name of a variable or a function, because it's already reserved.
In the solution zip_ is a generator. I did it in order to not keep too much data in memory. If you need an exact list, just convert it as I've done in the print-statement.
In case of wrong arguments the function may go into an infinite loop. For example, if you call zip_("b", "a"). But actually it's easy to fix by adding a few lines if it's necessary.

This is a base-26 number system and here is how I would solve it. And also zip is a python built-in function, probably better not to redefine it.
def alphaToNumber(s):
r = 0
for x in s:
r *= 26
r += ord(x) - 96
return r
def numberToAlpha(n, result):
head = (n - 1) // 26
tail = chr((n - 1) % 26 + 97)
if head == 0:
return tail + result
else:
return numberToAlpha(head, tail + result)
def gen(start, end):
start_n = alphaToNumber(start)
end_n = alphaToNumber(end)
return [numberToAlpha(x, "") for x in range(start_n, end_n + 1)]
print(gen("a", "ad"))
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'aa', 'ab', 'ac', 'ad']

Related

Python how to access list item with a variable

I searched for this for a while tonight, but I didn't find anything... or maybe I'm too noob to understand?
Well here's my code, the question will follow, I think that you'll understand it better if I show you the code now:
lencount = 1
decrypted = []
while lencount <= length:
if intlist[lencount] == 2:
print('OK!')
decrypted.append(alphabet[2])
lencount += 1
print(decrypted)
Here's the whole code, if you want to see:
import os
# os.system("multimon-ng -a dtmf -q | grep -x -E '[0-9]+' --line-buffered >> $HOME/dtmf.txt")
file=open('/home/nicolasmailloux/dtmf.txt')
line=file.read()
text = line.replace("\n", "")
n = 2
chars = [c for c in text]
ngrams = []
for i in range(len(chars)-n + 1):
ngram = "".join(chars[i:i+n])
ngrams.append(ngram)
intlist = list(map(int, ngrams))
length = int(len(intlist))
print(length)
print(intlist)
# Defining some variables
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
lencount = 1
decrypted = []
while lencount <= length:
if intlist[lencount] == 2:
print('OK!')
decrypted.append(alphabet[2])
lencount += 1
print(decrypted)
The thing is that the amount of items in the lists is variable and I can't access it (why??) by using a variable in intlist[lencount]. This is confusing. Here's the output I have when I try to run the program:
if intlist[lencount] == 2:
IndexError: list index out of range
Thanks for helping!
This occurs because the len() function returns:
The length (the number of items) of an object.
However, lists are zero-indexed in Python, meaning that some_list[len(some_list)] will always be out of range, because len() does not take zero-indexing into account.
Because of this, the conditional while lencount <= length allows intlist[len(intlist)] to occur, which by definition is out of range. This can be solved with the following:
while lencount < length:
if intlist[lencount] == 2:
print('OK!')
...
Additionally, assuming you want to go through all values of intlist, lencount should be initialized to 0, so:
lencount = 0
Here's the problem:
length = int(len(intlist))
in combination with
while lencount <= length:
if intlist[lencount] == 2:
lencount=length is out of range (indices go from 0, ..., len(list)-1).
lencount = 1
problem is your lencount starting with 1
your while loop is something like that,
lencount = 30
if intlist[31] == 2:
but inlist len is 30,
you are trying to find index 31

How do I recursively reverse a list in python without an aux fnc

Write a function that reverses a string. The input string is given as an array of characters char[].
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
You may assume all the characters consist of printable ascii characters.
Example:
Input: ["h","e","l","l","o"]
Output: ["o","l","l","e","h"]
I'm fairly new to recursion so I looked up some possible solutions but I'm not sure why this one isn't outputting the desired result as it looks like it runs.
class Solution(object):
def reverseString(self, s):
"""
:type s: List[str]
:rtype: None Do not return anything, modify s in-place instead.
"""
if not s:
return []
else:
return [s[-1]] + self.reverseString(s[:-1])
This is not quite as elegant of a solution, but it works.
def rev(arr, i=0):
if i >= len(arr) // 2:
return
arr[i], arr[-(i + 1)] = arr[-(i + 1)], arr[i]
rev(arr, i + 1)
test = ["t", "e", "s", "t"]
>>> test
['t', 'e', 's', 't']
>>> rev(test)
['t', 's', 'e', 't']
Recursive implementation :
>>> L = ["h","e","l","l","o"]
>>> L
['h', 'e', 'l', 'l', 'o']
reverse = lambda L: (reverse (L[1:]) + L[:1] if L else [])
>>> print(reverse(L))
['o', 'l', 'l', 'e', 'h']

About time complexity of the code snippet

I am trying to solve a question on Pramp:
Implement a function reverseWords that reverses the order of the words in the array in the most efficient manner.
Ex: arr = [ 'p', 'e', 'r', 'f', 'e', 'c', 't', ' ',
'm', 'a', 'k', 'e', 's', ' ',
'p', 'r', 'a', 'c', 't', 'i', 'c', 'e' ]
output: [ 'p', 'r', 'a', 'c', 't', 'i', 'c', 'e', ' ',
'm', 'a', 'k', 'e', 's', ' ',
'p', 'e', 'r', 'f', 'e', 'c', 't' ]
The Python-like pseudo-code which they have given is as follows:
function reverseWords(arr):
# reverse all characters:
n = arr.length
mirrorReverse(arr, 0, n-1)
# reverse each word:
wordStart = null
for i from 0 to n-1:
if (arr[i] == ' '):
if (wordStart != null):
mirrorReverse(arr, wordStart, i-1)
wordStart = null
else if (i == n-1):
if (wordStart != null):
mirrorReverse(arr, wordStart, i)
else:
if (wordStart == null):
wordStart = i
return arr
# helper function - reverses the order of items in arr
# please note that this is language dependent:
# if are arrays sent by value, reversing should be done in place
function mirrorReverse(arr, start, end):
tmp = null
while (start < end):
tmp = arr[start]
arr[start] = arr[end]
arr[end] = tmp
start++
end--
They say that the time complexity is O(n), essentially because they are traversing the array twice with a constant number of actions for each item. Co-incidentally, I came up with the exact same approach using stringstreams in C++, but thought that it was not efficient!
I think the time complexity of this snippet should be O(mn), where m is the number of words in the string and n is the average number of alphabets in each word. This is so because we iterate over all the elements in the input and in the worst case, mirrorReverse() visits all the elements again for reversing, for a given i.
Which is correct?
In O(n), n refers to the length of the input (total characters), not the quantity of words. I suspect you're confused because the code uses a variable n in the latter sense.
Note the explanation: "traversing the array ...". "The array" consists of individual characters.
The implementation seems a bit silly to me; it's much more readable to:
Join letter groups into words.
Reverse the word order (including spaces) with a trivial list slice.
Expand the words back into characters.

What is the best way to generate all possible three letter strings?

I am generating all possible three letters keywords e.g. aaa, aab, aac.... zzy, zzz below is my code:
alphabets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
keywords = []
for alpha1 in alphabets:
for alpha2 in alphabets:
for alpha3 in alphabets:
keywords.append(alpha1+alpha2+alpha3)
Can this functionality be achieved in a more sleek and efficient way?
keywords = itertools.product(alphabets, repeat = 3)
See the documentation for itertools.product. If you need a list of strings, just use
keywords = [''.join(i) for i in itertools.product(alphabets, repeat = 3)]
alphabets also doesn't need to be a list, it can just be a string, for example:
from itertools import product
from string import ascii_lowercase
keywords = [''.join(i) for i in product(ascii_lowercase, repeat = 3)]
will work if you just want the lowercase ascii letters.
You could also use map instead of the list comprehension (this is one of the cases where map is still faster than the LC)
>>> from itertools import product
>>> from string import ascii_lowercase
>>> keywords = map(''.join, product(ascii_lowercase, repeat=3))
This variation of the list comprehension is also faster than using ''.join
>>> keywords = [a+b+c for a,b,c in product(ascii_lowercase, repeat=3)]
from itertools import combinations_with_replacement
alphabets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
for (a,b,c) in combinations_with_replacement(alphabets, 3):
print a+b+c
You can also do this without any external modules by doing simple calculation.
The PermutationIterator is what you are searching for.
def permutation_atindex(_int, _set, length):
"""
Return the permutation at index '_int' for itemgetter '_set'
with length 'length'.
"""
items = []
strLength = len(_set)
index = _int % strLength
items.append(_set[index])
for n in xrange(1,length, 1):
_int //= strLength
index = _int % strLength
items.append(_set[index])
return items
class PermutationIterator:
"""
A class that can iterate over possible permuations
of the given 'iterable' and 'length' argument.
"""
def __init__(self, iterable, length):
self.length = length
self.current = 0
self.max = len(iterable) ** length
self.iterable = iterable
def __iter__(self):
return self
def __next__(self):
if self.current >= self.max:
raise StopIteration
try:
return permutation_atindex(self.current, self.iterable, self.length)
finally:
self.current += 1
Give it an iterable object and an integer as the output-length.
from string import ascii_lowercase
for e in PermutationIterator(ascii_lowercase, 3):
print "".join(e)
This will start from 'aaa' and end with 'zzz'.
chars = range(ord('a'), ord('z')+1);
print [chr(a) + chr(b) +chr(c) for a in chars for b in chars for c in chars]
We could solve this without the itertools by utilizing two function definitions:
def combos(alphas, k):
l = len(alphas)
kRecur(alphas, "", l, k)
def KRecur(alphas, prfx, l, k):
if k==0:
print(prfx)
else:
for i in range(l):
newPrfx = prfx + alphas[i]
KRecur(alphas, newPrfx, l, k-1)
It's done using two functions to avoid resetting the length of the alphas, and the second function self-iterates itself until it reaches a k of 0 to return the k-mer for that i loop.
Adopted from a solution by Abhinav Ramana on Geeks4Geeks
Well, i came up with that solution while thinking about how to cover that topic:
import random
s = "aei"
b = []
lenght=len(s)
for _ in range(10):
for _ in range(length):
password = ("".join(random.sample(s,length)))
if password not in b:
b.append("".join(password))
print(b)
print(len(b))
Please let me describe what is going on inside:
Importing Random,
creating a string with letters that we want to use
creating an empty list that we will use to put our combinations in
and now we are using range (I put 10 but for 3 digits it can be less)
next using random.sample with a list and list length we are creating letter combinations and joining it.
in next steps we are checking if in our b list we have that combination - if so, it is not added to the b list. If current combination is not on the list, we are adding it to it. (we are comparing final joined combination).
the last step is to print list b with all combinations and print number of possible combinations.
Maybe it is not clear and most efficient code but i think it works...
print([a+b+c for a in alphabets for b in alphabets for c in alphabets if a !=b and b!=c and c!= a])
This removes the repetition of characters in one string

My python method isn't working correctly, skipping items in a list (playfair cipher)

I'm trying to write a method that takes a key and an alphabet and creates a playfair cipher box. For those of you that don't know what that is, It takes the key and puts it in a 5 x 5 grid of letters, spilling onto the next line if neccessary, and then adds the rest of the letters of the alphabet. Each letter is only supposed to appear in the box once. I'm trying to do this with a list with 5 internal lists, each with 5 items. the only problem is that where the method is supposed to skip letters, it isn't. Here is the method and the output, can anyone help me?
def makePlayFair(key, alpha):
box = []
#join the key and alphabet string so that you only have to itterate over one string
keyAlpha = ""
keyAlpha = keyAlpha.join([key, alpha])
ind = 0
for lines in range(5):
line = []
while len(line) < 5:
if isIn(keyAlpha[ind], box) or isIn(keyAlpha[ind], line):
print(isIn(keyAlpha[ind],box))
ind += 1
continue
else:
line.append(keyAlpha[ind])
ind += 1
box.append(line)
return box
def isIn(item, block):
there = None
for each_item in block:
if type(each_item) == type([]):
for nested_item in each_item:
if item == nested_item:
there = True
break
else:
there = False
else:
if item == each_item:
there = True
break
else:
there = False
return there
>>> makePlayFair("hello", alphabet) #alphabet is a string with all the letters in it
> `[['h', 'e', 'l', 'o', 'a'], ['b', 'c', 'd', 'f', 'g'], ['h', 'i', 'j', 'k', 'l'], ['m', 'n', 'o', 'p', 'q'], ['r', 's', 't', 'u', 'v']]`
Thanks for your help in advance!
cheers, brad
Your problem is in isIn:
Your break statement only breaks out of the inner for loop. The code then continues to iterate over the second for loop. This means that only the last one is considered. You have to make sure that you exit out of both loops for this to work correctly.
The entire process can be made simpler by doing something like:
def makePlayFair(key, alpha):
letters = []
for letter in key + alpha:
if letter not in letters:
letters.append(letter)
box = []
for line_number in range(5):
box.append( letters[line_number * 5: (line_number+1) * 5])
Make the list of letters first, then break them up into the 5x5 grid:
def takeNatATime(n, seq):
its = [iter(seq)]*n
return zip(*its)
def makePlayfair(s):
keystr = []
for c in s + "abcdefghijklmnopqrstuvwxyz":
if c not in keystr:
keystr.append(c)
return list(takeNatATime(5, keystr))
print makePlayfair("hello")
Prints:
[('h', 'e', 'l', 'o', 'a'), ('b', 'c', 'd', 'f', 'g'), ('i', 'j', 'k', 'm', 'n'), ('p', 'q', 'r', 's', 't'), ('u', 'v', 'w', 'x', 'y')]
Here's my attempt:
#!/usr/bin/env python
from string import ascii_uppercase
import re
def boxify(key):
cipher = []
key = re.sub('[^A-Z]', '', key.upper())[:25]
for i in range(max(25 - len(key), 0)):
for letter in ascii_uppercase:
if letter not in key:
key += letter
break
for i in range(0, 25, 5):
cipher.append(list(key[i:i+5]))
return cipher
if __name__ == "__main__":
print boxify("This is more than twenty-five letters, i'm afraid.")
print boxify("But this isn't!")

Categories