Recursive code to create small shift dictionary - python

I have the following attempt at a recursive function to create a shift table within a dictionary:
def createShiftTable(alphabet,T,shiftT):
if not T:
for char in alphabet:
shiftT[char] = len(T)+1
return shiftT
else:
return createShiftTable(alphabet,T[1:],shiftT)
shiftT[T[0]] = len(T)
al=['a','b','c','d']
T = "aaabbdddaaba"
print(createShiftTable(al,T,{}))
This is returning {'a': 1, 'c': 1, 'b': 1, 'd': 1}
I'd like it to return {'a': 1, 'c': 13, 'b': 2, 'd': 5}
Non-recursively the following works ok but how do I get the above recursive function working?
def createShiftTableX(alphabet,T):
shiftT={}
for char in al:
shiftT[char] = len(T)+1
for i in range(len(T)):
shiftT[T[i]] = len(T)-i
return shiftT
al=['a','b','c','d']
T = "aaabbdddaaba"
print(createShiftTableX(al,T))

How about this:
def createShiftTableX(alphabet, T):
l = len(T)
for c in alphabet:
i = T.find(c)
if i<0:
yield l
else:
yield i+1
al=['a','b','c','d']
T = "aaabbdddaaba"
print(list(createShiftTableX(al,T)))

You should run this in a debugger or put in some logging or print statements to see what it is actually doing.

Related

Making dictionary item frequency

I want to know why it's not counting the element ?
I made an key then in each iteration it should increase the value.
def isAnagram( s, t):
if len(s) != len(t):
return False
d_1 = {}
d_2 = {}
for i in range(len(s)):
d_1[s[i]] =+ 1
d_2[t[i]] =+ 1
print(d_1)
print(d_2)
return True if d_1 == d_2 else False
s = 'aabb'
t = 'bbaa'
print(isAnagram(s,t))
OUTPUT :
d_1 = {'a': 1, 'b': 1}
d_2 = {'b': 1, 'a': 1}
First, you have an error, to add value to the variable you need to do
d_1[s[i]] += 1
d_2[t[i]] += 1
Second, the function fails because you need to define the key and value before modifying the value.
Working function:
def isAnagram( s, t):
if len(s) != len(t):
return False
d_1 = {}
d_2 = {}
for i in range(len(s)):
try: # checking if the character in the dictionary exists
d_1[s[i]]
except KeyError:
d_1[s[i]] = 0
try: # checking if the character in the dictionary exists
d_2[t[i]]
except KeyError:
d_2[t[i]] = 0
d_1[s[i]] += 1
d_2[t[i]] += 1
print(d_1)
print(d_2)
return True if d_1 == d_2 else False
s = 'aabb'
t = 'bbaa'
print(isAnagram(s,t))
as mentioned in comments. you should use += instead of =+.
+= means adding the value to the previous value stored. while
=+ here means = (assigning the value)
def isAnagram(s, t):
if len(s) != len(t):
return False
d_1 = {}
d_2 = {}
for i in range(len(s)):
d_1[s[i]] += 1
d_2[t[i]] += 1
print(d_1)
print(d_2)
return True if d_1 == d_2 else False
s = 'aabb'
t = 'bbaa'
print(isAnagram(s,t))
add a .lower() to Domagoj's answer to be case-insensitive.
so this will also work:
s = 'Betrug'.lower()
t = 'Erbgut'.lower()
{'b': 1, 'e': 1, 't': 1, 'r': 1, 'u': 1, 'g': 1}
{'e': 1, 'r': 1, 'b': 1, 'g': 1, 'u': 1, 't': 1}
True
Don’t reinvent the wheel!
from collections import Counter
def isAnagram(s, t):
return Counter(s) == Counter(t)
s = "aabb"
t = "bbaa"
print(isAnagram(s,t)) # => True

Dictionary/recursive/ counting parts (exercise[LOGIC])

problem: create a recursive function that given an input key, would return the amount of basic components to build the given input key.
EX 1) input = "Engine"
output = Engine ==> metal: 3, rubber: 2
EX 2) input = "metal"
output = metal ==> metal: 1
EX 3) input = "piston"
output = piston ==> metal: 1, rubber: 1
car= {
"Engine" : ["pistons", "timing belt", "metal" ,"metal"],
"Pistons" : ["Metal", "rubber"],
"timing belt" : ["rubber"],
"metal" : [],
"rubber" : []
}
my code has different variable names and key name, but it's the same idea
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#above here its user input
counter_dictio = {
'A': [],
'B': [],
'C': [],
'D': [],
'E': [],
'F': []
}
def desamble(key, dictionary):
#check if array is empty
#ccounter +=1
if (len(dictionary[key])) == 0:
counter_dictio[key].append(key)
#if array is populated
#enter to this array
#desample(i, dictionary)
else:
for i in dictionary[key]:
desamble(i, dictionary)
key = "A"
desamble(key, parts)
One way to go is:
from collections import Counter
car= {
"engine": ["pistons", "timing belt", "metal", "metal"],
"pistons": ["metal", "rubber"],
"timing belt": ["rubber"],
"metal": [],
"rubber": []
}
def ingredients(key, dct):
if dct[key] == []:
yield key
else:
for sub_part in dct[key]:
yield from ingredients(sub_part, dct)
print(*ingredients('engine', car)) # metal rubber rubber metal metal
print(Counter(ingredients('engine', car))) # Counter({'metal': 3, 'rubber': 2})
ingredients makes a generator of ingredients, so you can use Counter to count them.
Here is code that will group by your components
from collections import defaultdict
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D', 'E', 'F'],
'D': [],
'E': ['B', 'D'],
'F': []
}
result = defaultdict(dict)
for k, v in parts.items():
row = result[k] # used to create empty dict on initial empty list
for item in v:
if row.get(item) is None:
row[item] = 1
else:
row[item] += 1
This will result in following dict
{'A': {'B': 2, 'C': 1}, 'B': {}, 'C': {'D': 1, 'E': 1, 'F': 1}, 'D': {}, 'E': {'B': 1, 'D': 1}, 'F': {}}
another solution without using recursively, and for a predetermined list would be:
#==== user input====
key = "E"
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#====end user input=====
#Function for the predetermined dictionary
def desamble(key, dictionary):
if key == "B" or key == "D" or key == "F":
print(key + "==> " + key + ": 1")
elif key == "E":
print(key + "==> " + "B: 1, D: 1" )
elif key == "C":
print(key + "==> " + "B: 1, D: 2, F: 1" )
elif key == "A":
print(key + "==> " + "B: 3, D: 2, F: 1" )
else:
print("Key " + key + " is not defined in dictionary")
#====end====
desamble(key, parts)
Another recursive way to solve this problem, adding a solution for the problem of circularity, meaning that in case that the parts call to eachother.
EX)
dictionary = {
"A": "B",
"B": "A"
}
from typing iport Counter
def func(key, dictionary, current=None):
if not current:
current = set() # could also be a list
if key in current:
raise ValueError # or whichever
if not dictionary.get(key):
return [key]
ret = []
for subkey in dictionary[key]:
ret.extend(func(subkey, dictionary, current.union({key})))
return ret
Print(Counter(func("A"), parts))
#by officerthegeeks

How to ignore a specific character in a string, and later use it?

Previously I asked a question as to how to separate characters in a string according to capital letters, or spaces. It was immediately answered. Working on the same piece of code and now I was wondering if it is possible to read an input and ignore integers, and later use it.
For example if the string is H2O, times the value of H twice and then add the value of O.
How to read two characters from an input string?
^^That's the link to my previous question I asked on same code, if useful.
import re
atomMass_Table = {'H': 1.00797, 'He': 4.00260, 'B': 10.81,'C': 12.011, 'N': 14.067, 'O': 15.9994,'F':
18.998403,'P': 30.97376, 'S': 32.06, 'K':39.0983, ' ': 0, None: 0}
TotalMass=0
elements=[ ]
mol=input("Enter a molecule:")
elements = re.findall('[A-Z][^A-Z]*', mol)
for a in elements:
if a == int:
element=None
atomicMass=atomMass_Table.get(a)
TotalMass=TotalMass+atomicMass
print (TotalMass)
Hope I'm not being too confusing :)
There are two solutions for you. I don't understand the solution of Thomas, but alec's solution is acceptable. However, it can't work if you have something like "C12H22O11". I give a solution which can fix it, take a look.
atomMass_Table = {'H': 1.00797, 'He': 4.00260, 'B': 10.81,'C': 12.011, 'N': 14.067,
'O': 15.9994,'F': 18.998403,'P': 30.97376, 'S': 32.06, 'K':39.0983, ' ': 0, None: 0, 'd': 0}
TotalMass=0
elements=[ ]
mol=input("Enter a molecule:")
if not mol[-1].isdigit():
mol += '1'
mol += 'd'
number = []
for a in mol:
if a.isdigit():
number.append(a)
else:
if not number:
value = atomMass_Table.get(a)
else:
TotalMass += value * int(''.join(number))
value = atomMass_Table.get(a)
number = []
print (TotalMass)
The solution for H2O is 18.01534 and C12H22O11 is 342.30074.
Hope this help!
What about this?
import re
atom_masses = {'H': 1.00797, 'He': 4.00260, 'B': 10.81, 'C': 12.011, 'N': 14.067, 'O': 15.9994, 'F':
18.998403, 'P': 30.97376, 'S': 32.06, 'K': 39.0983, ' ': 0, None: 0}
total_mass = 0
elements = []
mol = input("Enter a molecule:")
parts = re.findall('([A-Z][a-z]?)(\d)*', mol)
print(parts)
for element, count in parts:
if count == '':
count = 0
atomic_mass = atom_masses.get(element)
total_mass = total_mass + atomic_mass * float(count)
print (total_mass)
I changed the regex to divide the string into separate atoms and their count. It has to be a capital letter followed by an optional small letter and an optional number.
Additionally, i changed the variable name because they should be in small letter.
Use isalpha() to check if the element only contains letters. If not, you can use string slicing to get the value of the first character from the dictionary, and multiply by the second character.
atomMass_Table = {'H': 1.00797, 'He': 4.00260, 'B': 10.81,'C': 12.011, 'N': 14.067, 'O': 15.9994,'F':
18.998403,'P': 30.97376, 'S': 32.06, 'K':39.0983, ' ': 0, None: 0}
TotalMass = 0
mol = input("Enter a molecule: ")
elements = re.findall('[A-Z][^A-Z]*', mol)
for a in elements:
if a.isalpha():
TotalMass += atomMass_Table.get(a)
else:
TotalMass += atomMass_Table.get(a[0]) * int(a[1])
print(TotalMass)
Example:
Enter a molecule: H2O
18.01534
>>>
I extended a bit your Request and added a Class for each molecule.
A Class contains of three attributes:
raw_element
molecule structure
molecule mass
the class could be extended with a drawing of the molecule or other features.
The Class:
class molecule:
def __init__(self, raw_element, atom_mass_table):
self.amt = atom_mass_table
self.raw_element = raw_element
self.molecule_structur = self.get_struc()
self.molecule_mass = self.cal_mass()
def get_struc(self):
return [self._split_nodes(e) for e in self._split_in_nodes()]
def cal_mass(self):
sum = 0
for i in self.molecule_structur:
sum += self.amt[i[0]] * i[1]
return sum
def _split_nodes(self, node):
s = ""
n = ""
for l in node:
if l.isalpha():
s += l
elif l.isdigit():
n += l
if n is None:
n = 1
return (s, int(n))
def _split_in_nodes(self):
new_el = [e.isupper() for e in self.raw_element]
el_str = []
pos = 0
for _ in range(sum(new_el)):
if True in new_el[pos+1:]:
end = pos + new_el[pos+1:].index(True)+1
else:
end = len(new_el)
el_str.append(self.raw_element[pos:end])
pos = end
return el_str
The Class only needs:
raw element description - C12H22O11
your provided atom_mass_table - I didn't include it in the class, for easier expansion later on.
So to test the code:
if __name__ == "__main__":
element = input("Please enter your Molecule: ")
molecule = Molecule(element, atom_mass_table)
print(molecule.molecule_structur)
print(molecule.molecule_mass)
Input:
Please enter you Molecule: C32H128O64
Output:
[('C', 32), ('H', 128), ('O', 64)]
1537.33376
Hope it helps and when you further improve this Application, you can invite me to you GitHub Project.

How can I dynamically combine sets in python

I have a list of dictionaries and need to iterate through them and check for the keys that exist already. I have implemented a python code to manually calculate a score as below. In my code, I'm manually combining keys from previous dictionaries in each iteration. Iteration will start from dict11.
How can I change this code to automatically iterate through a dynamic number of dictionaries and in each iteration how can I combine the keys dynamically?
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
exist_score = 0
for key in dict11.keys() & dict10.keys():
exist_score += dict11[key]
for key in dict12.keys() & set(dict11.keys()).union(set(dict10.keys())):
exist_score += dict12[key]
for key in dict13.keys() & set(dict12.keys()).union(set(dict11.keys()).union(set(dict10.keys()))):
exist_score += dict13[key]
print(exist_score)
First you need to turn this into something that can be put into a loop. The same thing has to happen with dict11, dict12 and dict13:
# same definition for dict10, dict11, dict12, dict13
exist_score = 0
seen_keys = set(dict10.keys())
for key in dict11.keys():
if key in seen_keys:
exist_score += dict11[key]
seen_keys.update(dict11.keys())
for key in dict12.keys():
if key in seen_keys:
exist_score += dict12[key]
seen_keys.update(dict12.keys())
for key in dict13.keys():
if key in seen_keys:
exist_score += dict13[key]
seen_keys.update(dict13.keys())
This should do the same thing as your script. Now you can put that into a loop …
# same definition for dict10, dict11, dict12, dict13
exist_score = 0
seen_keys = set(dict10.keys())
other_dicts = [dict11, dict12, dict13]
for d in other_dicts:
for key in d.keys():
if key in seen_keys:
exist_score += d[key]
seen_keys.update(d.keys())
It makes the most sense to keep dicts in a list themselves. Putting this logic into a function is also a no-brainer.
dicts = [
{'A': 1, 'C': 2},
{'B': 3, 'C': 4},
{'A': 5, 'E': 6, 'F': 7},
{'G': 8, 'E': 9}
]
def score_dicts(dicts):
score = 0
all_keys = set()
for d in dicts:
keys = d.keys()
for key in keys & all_keys:
score += d[key]
all_keys.update(keys)
return score
exist_score = score_dicts(dicts)
If you need to update the score periodically (one dict at a time), you can maintain the state in either a class or a closure.
Class:
class DictScorer():
def __init__(self):
self.exist_score = 0
self.all_keys = set()
def score(self, d):
keys = d.keys()
for key in keys & self.all_keys:
self.exist_score += d[key]
self.all_keys.update(keys)
return self.exist_score
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
scorer = DictScorer()
exist_score = scorer.score(dict10)
print(exist_score)
exist_score = scorer.score(dict11)
print(exist_score)
exist_score = scorer.score(dict12)
print(exist_score)
exist_score = scorer.score(dict13)
print(exist_score)
Closure:
# returns a scorer which can
# be used incrementally
def create_dict_scorer():
score = 0
all_keys = set()
def dict_scorer(d):
nonlocal score
keys = d.keys()
for key in keys & all_keys:
score += d[key]
all_keys.update(keys)
return score
return dict_scorer
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
scorer = create_dict_scorer()
exist_score = scorer(dict10)
print(exist_score)
exist_score = scorer(dict11)
print(exist_score)
exist_score = scorer(dict12)
print(exist_score)
exist_score = scorer(dict13)
print(exist_score)
Short magic with slicing and set operations:
dicts = [dict10, dict11, dict12, dict13]
exist_score = 0
for i, d in enumerate(dicts[:0:-1]):
offset = -(i - 2)
exist_score += sum(d[k] for k in d.keys() & set().union(*dicts[offset::-1]))
print(exist_score)
dicts[:0:-1] - slice of dictionaries in reversed order excluding the 1st one
-(i - 2) - negative offset to get consecutive "backward" slices for further set unions
The output (the same as in your initial approach):
18

Change money with python dynamic programming

Here are two programs for change money problem. The first one is only a recursion program that get all combinations and the second one is using dynamic programming. HOWEVER, i get into trouble when I am working on second one. It is supposed to be faster than the first one, but my program runs FOREVER to do it. I am pretty sure that I am using the dynamic programming, but i don't know what's the problem in it?
Notes: Total is the money going to be changed, units is a list with different values and stored is a dictionary to store the value of a step.
First:
def changeMoney(total, units):
if ( total == 0 ):
return [{}]
elif ( total < 0 ):
return []
else:
n = len(units)
ret = []
for i in range(0,n):
sols = changeMoney(total-units[i],units[i:n])
for sol in sols:
if ( units[i] in sol ):
sol[units[i]] += 1
else:
sol[units[i]] = 1
ret.append(sol)
return ret
print(dpChangeMoney(300,[100,50,20,10,5,2,1],{}))
Second:
import copy
def dpChangeMoney(total, units, stored):
key = ".".join(map(str,[total] + units))
if key in stored:
return stored[key]
else:
if ( total == 0 ):
return [{}]
elif ( total < 0 ):
return []
else:
n = len(units)
for i in range(0,n):
sols = copy.deepcopy(dpChangeMoney(total-
units[i],units[i:n], stored))
for sol in sols:
if ( units[i] in sol ):
sol[units[i]] += 1
else:
sol[units[i]] = 1
if key in stored:
if sol not in stored[key]:
stored[key] += [sol]
else:
stored[key] = [sol]
return stored[key]
print(dpChangeMoney(300,[100,50,20,10,5,2,1],{}))
Here's a much faster way to do this:
def dpChangeMoney(total, units, stored, min_ix=0):
if total < 0:
return []
if total == 0:
return [{}]
if min_ix == len(units):
return []
key = (total, min_ix)
if key in stored:
return stored[key]
sol_list = []
u = units[min_ix]
for c in range(total // u + 1):
sols = dpChangeMoney(total - c*u, units, stored, min_ix + 1)
for sol in sols:
if c > 0:
sol2 = sol.copy()
sol2[u] = c
else:
sol2 = sol
sol_list.append(sol2)
stored[key] = sol_list
return sol_list
If invoked as follows, it prints the number of solutions for the specified case:
print(len(dpChangeMoney(300, [100,50,20,10,5,2,1], {})))
The result is:
466800
On my system this took well under a second to run. (Of course, you could print the actual solutions, but there are a lot!)
To see the actual solutions for a total of 10:
print(dpChangeMoney(10, [100,50,20,10,5,2,1], {}))
The result is:
[{1: 10}, {1: 8, 2: 1}, {1: 6, 2: 2}, {1: 4, 2: 3}, {1: 2, 2: 4}, {2: 5}, {1: 5, 5: 1}, {1: 3, 2: 1, 5: 1}, {1: 1, 2: 2, 5: 1}, {5: 2}, {10: 1}]
i just figure out what is the problem in my algorithm. I will update a much faster algorithm after the due date. Thanks for your suggestions and instructions. E

Categories

Resources