Using brute force a dictionary of lists - python

I have a task that has to use brute force to solve.
The data is in a python dictionary where one value from each key is used to get correctly answer a sum is part of a wider solution so in that context there is only one solution but in the example, I am giving there is a possibility for multiple solutions I suppose. So let's just assume that the first solution is the correct one.
So for example say the target is 9036 The "rules" are that all we can do is addition and have to take one element from each list within the dictionary.
Therefore 9036 can be calculated from the dictionary below as x:9040,y:1247,w:242,z:-1493
I have been trying to achieve this via a loop but I cant get the code to "loop" how I want it to which is along the lines of iterating over x[0],y[0],w[0],z[0] where 0 is just the first element in the list in the first itteration and then doing x[0],y[1],w[0],z[0], x[0],y[1],w[1],z[0], x[0],y[1],w[1],z[1]... until it has solved the problem.
I have not added any code as I was simply running a loop that never got anywhere near what I needed it to do and as I have no real experience with these sorts of algorithms / brute-forcing I was hoping someone could help point me in the right direction.
EDIT::: I have added the dictionary so that a solution can be provided but the dictionary size can vary so it needs to be a dynamic solution.
The dictionary:
{'x': [11909.0, 9040.0], 'y': [4345.0, 1807.0, 1247.0, 0.0, 6152.0, 4222.0, 123.0], 'w': [538.0, 12.0, 526.0, 0.0, 242.0, 1.0, 128.0, 155.0], 'z': [7149.0, 3003.0, 4146.0, 3054.0, 0.0, -51.0, 1010.0, 189.0, 182.0, -1493.0, 5409.0, -1151.0]}

With inputs from https://stackoverflow.com/a/61335465/14066512 to iterate over dictionary.
permutations_dicts variable contains all the different types of "brute force" combinations
import itertools
keys, values = zip(*d.items())
permutations_dicts = [dict(zip(keys, v)) for v in itertools.product(*values)]
for i in permutations_dicts:
if sum(i.values())==9036:
print(i)
break
{'x': 9040.0, 'y': 1247.0, 'w': 242.0, 'z': -1493.0}

From what I understood you want to try all combinations of values for each key until you reach the right answer.
This will add all possible values for each key until it finds 9036:
my_dict = {'x': [11909.0, 9040.0], 'y': [4345.0, 1807.0, 1247.0, 0.0, 6152.0, 4222.0, 123.0], 'w': [538.0, 12.0, 526.0, 0.0, 242.0, 1.0, 128.0, 155.0], 'z': [7149.0, 3003.0, 4146.0, 3054.0, 0.0, -51.0, 1010.0, 189.0, 182.0, -1493.0, 5409.0, -1151.0]}
looping = True
for x in range(len(my_dict['x'])):
if not looping:
break
for y in range(len(my_dict['y'])):
if not looping:
break
for w in range(len(my_dict['w'])):
if not looping:
break
for z in range(len(my_dict['z'])):
addition = my_dict['x'][x] + my_dict['y'][y] + my_dict['w'][w] + my_dict['z'][z]
if addition == 9036:
new_dict = {'x':my_dict['x'][x], 'y':my_dict['y'][y], 'w':my_dict['w'][w], 'z':my_dict['z'][z]}
print(f"Correct answer is {new_dict}")
looping = False
break

Related

Understanding how Python Objects and Classes Work

I am trying to follow instructions from Effective Computation in Physics, a Field Guide to Research with Python book (by Anthony Scopatz and Kathryn Duff. In the OOP chapter(specifically page 136 in my version), I tried copying the code and ran the code below:
# import the Particle class from the particle module
from particle import Particle as p
# create an empty list to hold observed particle data
obs = []
# append the first particle
obs.append(p.Particle())
# assign its position
obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# append the second particle
obs.append(p.Particle())
# assign the position of the second particle
obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
# print the positions of each particle
print(obs[0].r)
print(obs[1].r)
The result is supposed to give the position typed in. However, the code did not work like this. Instead, I played around with the code and this code worked instead:
# Import the Particle class from the particle Module
from particle import Particle as p
# Create an empty list
obs = []
# Append first element
obs.append(p)
# Assign its position
obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# Append second particle
obs.append(particle())
# Assign second position
obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
print(obs[0].r)
print(obs[1].r)
I would love to understand why and what is going on. I am currently reviewing how OOP works and am using Python for now. Please respond! I want to learn how and why does this work!
I'm not sure I understood everything but if you want to turn your code in POO you can try something like this:
from particle import Particle as p
class Myclass:
def __init__(self):
# Create an empty list
self.obs = []
# Append first element
self.obs.append(p)
# Assign its position
self.obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# Append second particle
self.obs.append(particle())
# Assign second position
self.obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
#if you want to call getobs from here use :
#self.getobs1()
def getobs0(self):
print(self.obs[0].r)
def getobs1(self):
print(self.obs[1].r)
if __name__ == '__main__':
myClass = Myclass() #create an object of Myclass and call __init__()
myClass.getobs0()
myClass.getobs1()

Markov Chain: Find the most probable path from point A to point B

I have a transition matrix using dictionary
{'hex1': {'hex2': 1.0},
'hex2': {'hex4': 0.4, 'hex7': 0.2, 'hex6': 0.2, 'hex1': 0.2},
'hex4': {'hex3': 1.0},
'hex3': {'hex6': 0.3333333333333333, 'hex2': 0.6666666666666666},
'hex6': {'hex1': 0.3333333333333333,
'hex4': 0.3333333333333333,
'hex5': 0.3333333333333333},
'hex7': {'hex6': 1.0},
'hex5': {'hex3': 1.0}}
which shows the probability of going from a certain hex to another hex (e.g. hex1 has probability 1 going to hex2, hex2 has probability 0.4 going to hex4).
Taking the starting and endpoint, I want to find the path that has the highest probability.
The structure of the code will look like
def find_most_probable_path(start_hex, end_hex, max_path):
path = compute for maximum probability path from start_hex to end_hex
return path
where max_path is the maximum hexes to traverse. If there is no path within the max_path, return empty/null. Also, drop the path if goes back to the starting hex before reaching the ending hex.
Example would be
find_most_probable_path(hex2, hex3, 5)
>> "hex2,hex4,hex3"
The output can be a list of hexes or just concatenated strings.
You can treat your Markov Chain as a directed weighted graph, and use the probabilities as graph edges weights.
From this point, you can use the Dijkstra algorithm for a shortest path from two points on a weighted graph.
https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
I developed an algorithm but I have no idea about its efficiency but it works well.
table={'hex1': {'hex2': 1.0},
'hex2': {'hex4': 0.4, 'hex7': 0.2, 'hex6': 0.2, 'hex1': 0.2},
'hex4': {'hex3': 1.0},
'hex3': {'hex6': 0.3333333333333333, 'hex2': 0.6666666666666666},
'hex6': {'hex1': 0.3333333333333333,
'hex4': 0.3333333333333333,
'hex5': 0.3333333333333333},
'hex7': {'hex6': 1.0},
'hex5': {'hex3': 1.0}}
def find_most_probable_path(start_hex, end_hex, max_path=0):
assigned=[start_hex]
foundTrue=False
prob=[{"nodes":[start_hex],"prob":1,"length":1}]
if max_path==0:
status=False
else:
status=True
while status==True:
chn=[]
status=False
for i in prob:
if i["length"]<max_path:
lastElement=i["nodes"][-1]
for j in table[lastElement]:
if j not in assigned:
temp=i.copy()
js=temp["nodes"].copy()
js.append(j)
temp["nodes"]=js
temp["prob"]=temp["prob"]*table[lastElement][j]
temp["length"]+=1
#print(temp)
chn.append(temp)
status=True
maxv=0
for i in chn:
if i["prob"]>=maxv:
maxv=i["prob"]
added=i
if added["nodes"][-1]==end_hex:
foundTrue=True
status=False
assigned.append(added["nodes"][-1])
prob.append(added)
if foundTrue==True:
return prob[-1]["nodes"]
else:
return None
print(find_most_probable_path("hex2", "hex3",5))
The output will be:
['hex2', 'hex4', 'hex3']
If you want to see probability of path, you can change the part:
if foundTrue==True:
return prob[-1]["nodes"]
to:
if foundTrue==True:
return prob[-1]
Then the program give output like that:
{'nodes': ['hex2', 'hex4', 'hex3'], 'prob': 0.4, 'length': 3}

python float precision: will this increment work reliably?

I use the following code to dynamically generate a list of dictionaries of every combination of incremental probabilities associated with a given list of items, such that the probabilities sum to 1. For example, if the increment_divisor were 2 (leading to increment of 1/2 or 0.5), and the list contained 3 items ['a', 'b', 'c'], then the function should return
[{'a': 0.5, 'b': 0.5, 'c': 0.0},
{'a': 0.5, 'b': 0.0, 'c': 0.5},
{'a': 0.0, 'b': 0.5, 'c': 0.5},
{'a': 1.0, 'b': 0.0, 'c': 0.0},
{'a': 0.0, 'b': 1.0, 'c': 0.0},
{'a': 0.0, 'b': 0.0, 'c': 1.0}]
The code is as follows. The script generates the incrementer by calculating 1/x and then iteratively adds the incrementer to increments until the value is >= 1.0. I already know that python floats are imprecise, but I want to be sure that the last value in increments will be something very close to 1.0.
from collections import OrderedDict
from itertools import permutations
def generate_hyp_space(list_of_items, increment_divisor):
"""Generate list of OrderedDicts filling the hypothesis space.
Each OrderedDict is of the form ...
{ i1: 0.0, i2: 0.1, i3: 0.0, ...}
... where .values() sums to 1.
Arguments:
list_of_items -- items that receive prior weights
increment_divisor -- Increment by 1/increment_divisor. For example,
4 yields (0.0, 0.25, 0.5, 0.75, 1.0).
"""
_LEN = len(list_of_items)
if increment_divisor < _LEN: # permutations() returns None if this is True
print('WARN: increment_divisor too small, so was reset to '
'len(list_of_items).', file=sys.stderr)
increment_divisor = _LEN
increment_size = 1/increment_divisor
h_space = []
increments = []
incremental = 0.0
while incremental <= 1.0:
increments.append(incremental)
incremental += increment_size
for p in permutations(increments, _LEN):
if sum(p) == 1.0:
h_space.append(OrderedDict([(list_of_items[i], p[i])
for i in range(_LEN)]))
return h_space
How large can the increment_divisor be before the imprecision of float breaks the reliability of the script? (specifically, while incremental <= 1.0 and if sum(p) == 1.0)
This is a small example, but real use will involve much larger permutation space. Is there a more efficient/effective way to achieve this goal? (I already plan to implement a cache.) Would numpy datatypes be useful here for speed or precision?
The script generates the incrementer by calculating 1/x and then iteratively adds the incrementer to increments until the value is >= 1.0.
No, no, no. Just make a list of [0/x, 1/x, ..., (x-1)/x, x/x] by dividing each integer from 0 to x by x:
increments = [i/increment_divisor for i in range(increment_divisor+1)]
# or for Python 2
increments = [1.0*i/increment_divisor for i in xrange(increment_divisor+1)]
The list will always have exactly the right number of elements, no matter what rounding errors occur.
With NumPy, this would be numpy.linspace:
increments = numpy.linspace(start=0, stop=1, num=increment_divisor+1)
As for your overall problem, working in floats at all is probably a bad idea. You should be able to do the whole thing with integers and only divide by increment_divisor at the end, so you don't have to deal with floating-point precision issues in sum(p) == 1.0. Also, itertools.permutations doesn't do what you want, since it doesn't allow repeated items in the same permutation.
Instead of filtering permutations at all, you should use an algorithm based on the stars and bars idea to generate all possible ways to place len(list_of_items) - 1 separators between increment_divisor outcomes, and convert separator placements to probability dicts.
Thanks to #user2357112 for...
...pointing out the approach to work with ints until the last step.
...directing me to stars and bars approach.
I implemented stars_and_bars as a generator as follows:
def stars_and_bars(n, k, the_list=[]):
"""Distribute n probability tokens among k endings.
Generator implementation of the stars-and-bars algorithm.
Arguments:
n -- number of probability tokens (stars)
k -- number of endings/bins (bars+1)
"""
if n == 0:
yield the_list + [0]*k
elif k == 1:
yield the_list + [n]
else:
for i in range(n+1):
yield from stars_and_bars(n-i, k-1, the_list+[i])

Merging duplicate lists and deleting a field in each list depending on the value in Python

I am still a beginner in Python. I have a tuple to be filtered, merged and sorted.
The tuple looks like this:
id, ts,val
tup = [(213,5,10.0),
(214,5,20.0),
(215,5,30.0),
(313,5,60.0),
(314,5,70.0),
(315,5,80.0),
(213,10,11.0),
(214,10,21.0),
(215,10,31.0),
(313,10,61.0),
(314,10,71.0),
(315,10,81.0),
(315,15,12.0),
(314,15,22.0),
(215,15,32.0),
(313,15,62.0),
(214,15,72.0),
(213,15,82.0] and so on
Description about the list: The first column(id)can have only these 6 values 213,214,215,313,314,315 but in any different order. The second column(ts) will have same values for every 6 rows. Third column(val) will have some random floating point values
Now my final result should be something like this:
result = [(5,10.0,20.0,30.0,60.0,70.0,80.0),
(10,11.0,21.0,31.0,61.0,71.0,81.0),
(15,82.0,72.0,32.0,62.0,22.0,12.0)]
That is the first column in each row is to be deleted. There should be only one unique row for each unique value in the second column. so the order of each result row should be:
(ts,val corresponding to id 213,val corresponding to 214, corresponding to id 215,val corresponding to 313,corresponding to id 314,val corresponding to 315)
Note : I am restricted to use only the standard python libraries. So panda, numpy cannot be used.
I tried a lot of possibilities but couldnt solve it. Please help me do this. Thanks in advance.
You can use itertools.groupby
from itertools import groupby
result=[]
for i,g in groupby(lst, lambda x:x[1]):
group= [i]+map(lambda x:x[-1],sorted(list(g),key=lambda x:x[0]))
result.append(tuple(group))
print result
Output:
[(5, 10.0, 20.0, 30.0, 60.0, 70.0, 80.0),
(10, 11.0, 21.0, 31.0, 61.0, 71.0, 81.0),
(15, 82.0, 72.0, 32.0, 62.0, 22.0, 12.0)]
With a slight change to your code you can fix it. If you change i[1] in ssd[cnt] to i[1] == ssd[cnt][0] your code may work. Also in else part you should add another list to ssd because you are creating another set of data. Also if the data should come according to their id's you should sort them by (ts,id). After applying the changes:
tup.sort( key = lambda x: (x[1],x[0]) )
ssd = [[]]
cnt = 0
ssd[0].append(tup[0][1])
for i in tup:
if i[1] == ssd[cnt][0]:
ssd[cnt].append(i[2])
else:
cnt = cnt + 1
ssd.append([])
ssd[cnt].append(i[1])
ssd[cnt].append(i[2])
Output
[[5, 10.0, 20.0, 30.0, 60.0, 70.0, 80.0],
[10, 11.0, 21.0, 31.0, 61.0, 71.0, 81.0],
[15, 82.0, 72.0, 32.0, 62.0, 22.0, 12.0]]
Here's a vanilla python solution, although I do think that using groupby is more pythonic. This does have the disadvantage that it has to build the dicts in memory, so it won't scale to a large tup list.
This does, however, obey the ordering requirement.
from collections import defaultdict
tup = ...
tup_dict = defaultdict(dict)
for id, ts, val in tup:
print id, ts, val
tup_dict[ts][id] = val
for tup_key in sorted(tup_dict):
id_dict = tup_dict[tup_key]
print tuple([tup_key] + [ id_dict[id_key] for id_key in sorted(id_dict)])
We want to iterate on a sorted instance of your tup, unpacking the items as we go, but first we need an auxiliary variable to store the keys and a variable to store our results
keys, res = [], []
for t0, t1, t2 in sorted(tup, key=lambda x:(x[1],x[0])):
the key argument is a lambda function that instructs thesorted` function to sort on the second and the first item of each element in the individual tuple --- so here we have the body of the loop
if t1 not in keys:
keys.append[t1]
res.append([t1])
that is, if the second integer in the tuple was not already processed, we have to memorize the fact that it's being processed and we want to add a new list in our result variable, that starts with the value of the second integer
To finish the operation on an individual tuple, we are sure that there is a list in res that starts with t1, indexing the aux variable we know the index of that list and so we can append the float to it...
i = keys.index(t1)
res[i].append(t2)
To have all of that in short
keys, res = [], []
for t0, t1, t2 in sorted(tup, key=lambda x:(x[1],x[0])):
if t1 not in keys:
keys.append[t1]
res.append([t1])
i = keys.index(t1)
res[i].append(t2)
Now, in res you have a list of lists, if you really need a list of tuples you can convert with a list comprehension
res = [tuple(elt) for elt in res]
adding to the answer of #Ahsanul Haque he also need it in order so instead of list(g) do sorted(g,key=lambda y:y[0]) you can also do the use tuple from the start
for i,g in groupby(tup,lambda x:x[1]):
gro = (i,) + tuple(map(lambda x:x[-1],sorted(g,key=lambda y:y[0])))
resul.append(gro)

my str(float) gets broken on histogram dictionary conversion, how do I stop this?

When attempting to histogram a list of numbers(in str formats) all of my numbers get broken up
for instance
a = ['1','1.5','2.5']
after running my histogram function
my dictionary looks like
{'1': 2, '2': 1, '5': 2, '.': 2}
my histogram function is
def histogram(a):
d = dict()
for c in a:
d[c] = d.get(c,0)+1
return d
I'm doing a project for school and have everything coded in, but when I get to doing the mode portion and I use numbers that aren't specifically int I get the above returns
How can I adjust/change this so it accepts the strings exactly as typed
Python 2.7 on Windows 7x64
You can convert each string element to a float before passing it your histogram function.
a = ['1','1.5','2.5']
a = [float(i) for i in a]
def histogram(a):
d = dict()
for c in a:
d[c] = d.get(c,0)+1
return d
print histogram(a)
There might be an error in your list definition. Running your code I get
{'1': 1, '1.5': 1, '2.5': 1}
If I change the definition of a from
a = ['1','1.5','2.5']
to
a = '1' '1.5' '2.5'
I get the output you showed us.
So please double check how your list is defined.
You can use something like this:
>>> a = ['1','1.5','2.5']
>>> dict.fromkeys(a, 0)
{'1': 0, '1.5': 0, '2.5': 0}
Now you can iterate over keys to set the corresponding value.
I have used the following dict comprehension to reduce my work.
>>> {key: float(key)+1 for key in a}
{'1': 2.0, '1.5': 2.5, '2.5': 3.5}
enjoy :)
The histgram function does work as it's written. If however you you inadvertently .join() your list your histogram with then histogram the resulting object. For instance... t = ['1.0','2.0','2.5'] and
s = s.join(t) s will then be == '1.02.02.5' and histogram(s) will count the decimals as values in the slice. My problem was that I had placed a .join() prior to calling histogram.
My appologies to anyone that wasted any real time on this.

Categories