backtracking not trying all possibilities - python

so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.

There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.

Related

How can we develop an algorithm to solve a problem of DP [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
could someone plz solve this question using python language?
You can use DP to solve it. Here is a good website for learning DP: https://www.geeksforgeeks.org/dynamic-programming/
# For example X=3, Y=5, D=24. If we know solution for D=21 (24-3) and D=19 (24-5), then we know for D=24 the solution is min(D=21, D=19) +1.
# And for D=19, we know it's min(D=16, D=14) +1. So all the way back to D=3 and D=5.
def sol(X,Y,D):
# dp list for storing the solution for each D.
# For inner list, index 0 represent the usage of X, index 1 represent the usage of Y.
dp = [[float('inf'), float('inf')] for _ in range(D+1)]
# Assume X <= D and Y <= D, it guarantees both X and Y can fit in D.
# for D=X, the solution is 1X, 0Y, so it's[1,0]
dp[X] = [1,0]
# for D=Y, the solution is 0X, 1Y, so it's[0,1]
dp[Y] = [0,1]
for i in range(min(X,Y)+1, D+1):
# If already has a solution, skip.
if dp[i][0] != float('inf'):
continue
# Current D= min(D-X, D-Y) + 1
if sum(dp[i-X]) < sum(dp[i-Y]):
dp[i][0] = dp[i-X][0]+1
dp[i][1] = dp[i-X][1]
else:
dp[i][0] = dp[i-Y][0]
dp[i][1] = dp[i-Y][1]+1
# Search for a solution backward.
for i in range(len(dp)-1, -1, -1):
if dp[i][0] != float('inf') and dp[i][1] != float('inf'):
return (D-i, dp[i][0], dp[i][1])
return (D-i, 0, 0)
print(sol(3,5,24))
Here is a simplified question for understanding DP:
'''
To make it easier understand, let's simplify the question: we just want to know the minimium number of tables to get to D.
For example, X=3, Y=5, D=15.
'''
def sol(X,Y,D):
# 1. We create a list of size D+1: dp = [None, None, None, ....], +1 just for easier calculation. Since the index starts at 0.
# float('inf') represents infinity number, just for easier to use below. You will see it later.
dp = [float('inf') for _ in range(D+1)]
# 2. Our base case: when D=3, we know X=1,Y=0 is the solution, so only need 1 table to get to D=3.
# Same for D=5: when D=5, we know X=0,Y=1 is the solution.
# So set our base case: dp[3] = 1, dp[5] = 1
dp[X] = 1
dp[Y] = 1
# 4. Now the list is dp = [None, None, None, 1, None, 1, None, ....]
# 5. For D < min(X, Y), that's for sure no solution.
# So we start calculating solution for each D > min(X,Y) (D=min(X,Y) is our base case)
for i in range(min(X,Y)+1, D+1):
# If already has a solution, skip. This is for preventing overwrite case such as Y can be formed by X.
# For example, X=3, Y=6, on dp[6] we don't want the solution from dp[6-3].
if dp[i] != float('inf'):
continue
# 7. Let's take example at D=8. The solution could D=3 + 1 table, or D=5 + 1 table.
# Since we want the minimum, so our solution for D=8 is: min(D-3, D-5) + 1
# Here is the reason we initiate value is float('inf') instead of None.
# We don't have to check if it's None before calculate.
# For example D=7, D-3 is None, D-5 is None. min(None, None) throws error,
# we have to check if it's None or not before calculate. But if we use float('inf'),
# min(float('inf'), float('inf')) + 1 is still float('inf').
dp[i] = min(dp[i-X], dp[i-Y]) + 1
# 8. Search for a smallest distance solution. For D=24, there is already a solution, so return dp[24]
# But some D don't have solution. For example D=7 doesn't have a solution, so we check, D=6, D=5.. until
# it finds a solution. And that's the smallest distance solution.
for i in range(len(dp)-1, -1, -1):
if dp[i] != float('inf'):
# first element is the distance, second is the number of tables.
return (D-i, dp[i])
return (D-i, 0)
print(sol(3,5,15))
i think this is what you are looking for
NOTE: THE VALUE OF X IS GREATER THAN Y.
def table_arrange(x,y,d):
list_of_lists = []
a = d//x
b = list(x for i in range(0,a))
while sum_of_list(b)+y <= d:
b.append(y)
array = b.copy()
list_of_lists.append(array)
list_of_lists = arrange(list_of_lists,b,x,y,d)
differnce = y
optimum = []
for lists in list_of_lists:
a = d - sum_of_list(lists)
if differnce > a:
optimum = lists
differnce = a
f = optimum.count(x)
g = optimum.count(y)
return [f,g,differnce]
def arrange(list_of_lists,b,x,y,d):
c = []
for i in range(b.count(x)):
b.pop(0)
while sum_of_list(b)+y <= d:
b.append(y)
c = b.copy()
list_of_lists.append(c)
return list_of_lists
def sum_of_list(list_):
a = 0
for i in list_:
a = a+i
return a
print(table_arrange(5,3,21))

Is it possible to go through all the if statements in the function?

def ShortestDistance(maps,i,j,short,k): #i,j-->current position.
x=0
y=0
for a in range(0, len(maps)):
for b in range(0, len(maps[0])):
if maps[a][b] == 2:
x = a
y = b
if (i==x) and (j==y):
short=min(short,k)
return short
maps[i][j]=3
if within(i+1,j) and possible(maps,i+1,j):
return ShortestDistance(maps,i+1,j,short,k+1)
if within(i, j+1) and possible(maps,i, j+1):
return ShortestDistance(maps,i,j+1,short,k+1)
if within(i-1,j) and possible(maps,i-1,j):
return ShortestDistance(maps,i-1,j,short,k+1)
if within(i,j-1) and possible(maps,i,j-1):
return ShortestDistance(maps,i,j-1,short,k+1)
maps[i][j]=0
result=ShortestDistance(maps,0,0,longest,0)
if result is None:
answer=-1
else:
answer=result
What I'm trying to do here is to have the function go through all the if statements in the function whether the previous if statement was true or not.
For instance, I want my 3rd if statement in my code to go through as well even if the 2nd one was true (in this case, it seems like the computer isn't going through the 3rd one).
My ultimate goal is to find the minimum value of k for ALL THE POSSIBLE CASES.
How would I be able to make it possible?
Be careful with return statements. Once the function encounters one it exits.
Try something like this instead:
def ShortestDistance(maps,i,j,short,k):
#...
distances = []
if within(i+1,j) and possible(maps,i+1,j):
distances.append(ShortestDistance(maps,i+1,j,short,k+1))
if within(i, j+1) and possible(maps,i, j+1):
distances.append(ShortestDistance(maps,i,j+1,short,k+1))
if within(i-1,j) and possible(maps,i-1,j):
distances.append(ShortestDistance(maps,i-1,j,short,k+1))
if within(i,j-1) and possible(maps,i,j-1):
distances.append(ShortestDistance(maps,i,j-1,short,k+1))
return min(distances)
Use this one:
def ShortestDistance(maps,i,j,short,k): #i,j-->current position.
x=0
y=0
for a in range(0, len(maps)):
for b in range(0, len(maps[0])):
if maps[a][b] == 2:
x = a
y = b
if (i==x) and (j==y):
short=min(short,k)
return short
maps[i][j]=3
resx = []
if within(i+1,j) and possible(maps,i+1,j):
resx.append(ShortestDistance(maps,i+1,j,short,k+1))
if within(i, j+1) and possible(maps,i, j+1):
resx.append(ShortestDistance(maps,i,j+1,short,k+1))
if within(i-1,j) and possible(maps,i-1,j):
resx.append(ShortestDistance(maps,i-1,j,short,k+1))
if within(i,j-1) and possible(maps,i,j-1):
resx.append(ShortestDistance(maps,i,j-1,short,k+1))
maps[i][j]=0
return min(resx)
result=ShortestDistance(maps,0,0,longest,0)
if result is None:
answer=-1
else:
answer=result

Python - speed up pathfinding

This is my pathfinding function:
def get_distance(x1,y1,x2,y2):
neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
old_nodes = [(square_pos[x1,y1],0)]
new_nodes = []
for i in range(50):
for node in old_nodes:
if node[0].x == x2 and node[0].y == y2:
return node[1]
for neighbor in neighbors:
try:
square = square_pos[node[0].x+neighbor[0],node[0].y+neighbor[1]]
if square.lightcycle == None:
new_nodes.append((square,node[1]))
except KeyError:
pass
old_nodes = []
old_nodes = list(new_nodes)
new_nodes = []
nodes = []
return 50
The problem is that the AI takes to long to respond( response time <= 100ms)
This is just a python way of doing https://en.wikipedia.org/wiki/Pathfinding#Sample_algorithm
You should replace your algorithm with A*-search with the Manhattan distance as a heuristic.
One reasonably fast solution is to implement the Dijkstra algorithm (that I have already implemented in that question):
Build the original map. It's a masked array where the walker cannot walk on masked element:
%pylab inline
map_size = (20,20)
MAP = np.ma.masked_array(np.zeros(map_size), np.random.choice([0,1], size=map_size))
matshow(MAP)
Below is the Dijkstra algorithm:
def dijkstra(V):
mask = V.mask
visit_mask = mask.copy() # mask visited cells
m = numpy.ones_like(V) * numpy.inf
connectivity = [(i,j) for i in [-1, 0, 1] for j in [-1, 0, 1] if (not (i == j == 0))]
cc = unravel_index(V.argmin(), m.shape) # current_cell
m[cc] = 0
P = {} # dictionary of predecessors
#while (~visit_mask).sum() > 0:
for _ in range(V.size):
#print cc
neighbors = [tuple(e) for e in asarray(cc) - connectivity
if e[0] > 0 and e[1] > 0 and e[0] < V.shape[0] and e[1] < V.shape[1]]
neighbors = [ e for e in neighbors if not visit_mask[e] ]
tentative_distance = [(V[e]-V[cc])**2 for e in neighbors]
for i,e in enumerate(neighbors):
d = tentative_distance[i] + m[cc]
if d < m[e]:
m[e] = d
P[e] = cc
visit_mask[cc] = True
m_mask = ma.masked_array(m, visit_mask)
cc = unravel_index(m_mask.argmin(), m.shape)
return m, P
def shortestPath(start, end, P):
Path = []
step = end
while 1:
Path.append(step)
if step == start: break
if P.has_key(step):
step = P[step]
else:
break
Path.reverse()
return asarray(Path)
And the result:
start = (2,8)
stop = (17,19)
D, P = dijkstra(MAP)
path = shortestPath(start, stop, P)
imshow(MAP, interpolation='nearest')
plot(path[:,1], path[:,0], 'ro-', linewidth=2.5)
Below some timing statistics:
%timeit dijkstra(MAP)
#10 loops, best of 3: 32.6 ms per loop
The biggest issue with your code is that you don't do anything to avoid the same coordinates being visited multiple times. This means that the number of nodes you visit is guaranteed to grow exponentially, since it can keep going back and forth over the first few nodes many times.
The best way to avoid duplication is to maintain a set of the coordinates we've added to the queue (though if your node values are hashable, you might be able to add them directly to the set instead of coordinate tuples). Since we're doing a breadth-first search, we'll always reach a given coordinate by (one of) the shortest path(s), so we never need to worry about finding a better route later on.
Try something like this:
def get_distance(x1,y1,x2,y2):
neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
nodes = [(square_pos[x1,y1],0)]
seen = set([(x1, y1)])
for node, path_length in nodes:
if path_length == 50:
break
if node.x == x2 and node.y == y2:
return path_length
for nx, ny in neighbors:
try:
square = square_pos[node.x + nx, node.y + ny]
if square.lightcycle == None and (square.x, square.y) not in seen:
nodes.append((square, path_length + 1))
seen.add((square.x, square.y))
except KeyError:
pass
return 50
I've also simplified the loop a bit. Rather than switching out the list after each depth, you can just use one loop and add to its end as you're iterating over the earlier values. I still abort if a path hasn't been found with fewer than 50 steps (using the distance stored in the 2-tuple, rather than the number of passes of the outer loop). A further improvement might be to use a collections.dequeue for the queue, since you could efficiently pop from one end while appending to the other end. It probably won't make a huge difference, but might avoid a little bit of memory usage.
I also avoided most of the indexing by one and zero in favor of unpacking into separate variable names in the for loops. I think this is much easier to read, and it avoids confusion since the two different kinds of 2-tuples had had different meanings (one is a node, distance tuple, the other is x, y).

Issues with using np.linalg.solve in Python

Below, I'm trying to code a Crank-Nicholson numerical solution to the Navier-Stokes equation for momentum (simplified with placeholders for time being), but am having issues with solving for umat[timecount,:], and keep getting the error "ValueError: setting an array element with a sequence". I'm extremely new to Python, does anyone know what I could do differently to avoid this problem?
Thanks!!
def step(timesteps,dn,dt,Numvpts,Cd,g,alpha,Sl,gamma,theta_L,umat):
for timecount in range(0, timesteps+1):
if timecount == 0:
umat[timecount,:] = 0
else:
Km = 1 #placeholder for eddy viscosity
thetaM = 278.15 #placeholder for theta_m for time being
A = Km*dt/(2*(dn**2))
B = (-g*dt/theta_L)*thetaM*np.sin(alpha)
C = -dt*(1/(2*Sl) + Cd)
W_arr = np.zeros(Numvpts+1)
D = np.zeros(Numvpts+1)
for x in (0,Numvpts): #creating the vertical veocity term
if x==0:
W_arr[x] = 0
D[x] = 0
else:
W_arr[x] = W_arr[x-1] - (dn/Sl)*umat[timecount-1,x-1]
D = W_arr/(4*dn)
coef_mat_u = Neumann_mat(Numvpts,D-A,(1+2*A),-(A+D))
b_arr_u = np.zeros(Numvpts+1) #the array of known quantities
umat_forward = umat[timecount-1,2:Numvpts]
umat_center = umat[timecount-1,1:Numvpts-1]
umat_backward = umat[timecount-1,0:Numvpts-2]
b_arr_u = np.zeros(Numvpts+1)
for j in (0,Numvpts):
if j==0:
b_arr_u[j] = 0
elif j==Numvpts:
b_arr_u[j] = 0
else:
b_arr_u[j] = (A+D[j])*umat_backward[j]*(1-2*A)*umat_center[j] + (A-D[j])*umat_forward[j] - C*(umat_center[j]*umat_center[j]) - B
umat[timecount,:] = np.linalg.solve(coef_mat_u,b_arr_u)
return(umat)
Please note that,
for i in (0, 20):
print(i),
will give result 0 20 not 0 1 2 3 4 ... 20
So you have to use the range() function
for i in range(0, 20 + 1):
print(i),
to get 0 1 2 3 4 ... 20
I have not gone through your code rigorously, but I think the problem is in your two inner for loops:
for x in (0,Numvpts): #creating the vertical veocity term
which is setting values only at zero th and (Numvpts-1) th index. I think you must use
for x in range(0,Numvpts):
Similar is the case in (range() must be used):
for j in (0,Numvpts):
Also, here j never becomes == Numvpts, but you are checking the condition? I guess it must be == Numvpts-1
And also the else condition is called for every index other than 0? So in your code the right hand side vector has same numbers from index 1 onwards!
I think the fundamental problem is that you are not using range(). Also it is a good idea to solve the NS eqns for a small grid and manually check the A and b matrix to see whether they are being set correctly.

Query long lists

I would like to query the value of an exponentially weighted moving average at particular points. An inefficient way to do this is as follows. l is the list of times of events and queries has the times at which I want the value of this average.
a=0.01
l = [3,7,10,20,200]
y = [0]*1000
for item in l:
y[int(item)]=1
s = [0]*1000
for i in xrange(1,1000):
s[i] = a*y[i-1]+(1-a)*s[i-1]
queries = [23,68,103]
for q in queries:
print s[q]
Outputs:
0.0355271185019
0.0226018371526
0.0158992102478
In practice l will be very large and the range of values in l will also be huge. How can you find the values at the times in queries more efficiently, and especially without computing the potentially huge lists y and s explicitly. I need it to be in pure python so I can use pypy.
Is it possible to solve the problem in time proportional to len(l)
and not max(l) (assuming len(queries) < len(l))?
Here is my code for doing this:
def ewma(l, queries, a=0.01):
def decay(t0, x, t1, a):
from math import pow
return pow((1-a), (t1-t0))*x
assert l == sorted(l)
assert queries == sorted(queries)
samples = []
try:
t0, x0 = (0.0, 0.0)
it = iter(queries)
q = it.next()-1.0
for t1 in l:
# new value is decayed previous value, plus a
x1 = decay(t0, x0, t1, a) + a
# take care of all queries between t0 and t1
while q < t1:
samples.append(decay(t0, x0, q, a))
q = it.next()-1.0
# take care of all queries equal to t1
while q == t1:
samples.append(x1)
q = it.next()-1.0
# update t0, x0
t0, x0 = t1, x1
# take care of any remaining queries
while True:
samples.append(decay(t0, x0, q, a))
q = it.next()-1.0
except StopIteration:
return samples
I've also uploaded a fuller version of this code with unit tests and some comments to pastebin: http://pastebin.com/shhaz710
EDIT: Note that this does the same thing as what Chris Pak suggests in his answer, which he must have posted as I was typing this. I haven't gone through the details of his code, but I think mine is a bit more general. This code supports non-integer values in l and queries. It also works for any kind of iterables, not just lists since I don't do any indexing.
I think you could do it in ln(l) time, if l is sorted. The basic idea is that the non recursive form of EMA is a*s_i + (1-a)^1 * s_(i-1) + (1-a)^2 * s_(i-2) ....
This means for query k, you find the greatest number in l less than k, and for a estimation limit, use the following, where v is the index in l, l[v] is the value
(1-a)^(k-v) *l[v] + ....
Then, you spend lg(len(l)) time in search + a constant multiple for the depth of your estimation. I'll provide a code sample in a little bit (after work) if you want it, just wanted to get my idea out there while I was thinking about it
here's the code -
v is the dictionary of values at a given time; replace with 1 if it's just a 1 every time...
import math
from bisect import bisect_right
a = .01
limit = 1000
l = [1,5,14,29...]
def find_nearest_lt(l, time):
i = bisect_right(a, x)
if i:
return i-1
raise ValueError
def find_ema(l, time):
i = find_nearest_lt(l, time)
if l[i] == time:
result = a * v[l[i]
i -= 1
else:
result = 0
while (time-l[i]) < limit:
result += math.pow(1-a, time-l[i]) * v[l[i]]
i -= 1
return result
if I'm thinking correctly, the find nearest is l(n), then the while loop is <= 1000 iterations, guaranteed, so it's technically a constant (though a kind of large one). find_nearest was stolen from the page on bisect - http://docs.python.org/2/library/bisect.html
It appears that y is a binary value -- either 0 or 1 -- depending on the values of l. Why not use y = set(int(item) for item in l)? That's the most efficient way to store and look up a list of numbers.
Your code will cause an error the first time through this loop:
s = [0]*1000
for i in xrange(1000):
s[i] = a*y[i-1]+(1-a)*s[i-1]
because i-1 is -1 when i=0 (first pass of loop) and both y[-1] and s[-1] are the last element of the list, not the previous. Maybe you want xrange(1,1000)?
How about this code:
a=0.01
l = [3.0,7.0,10.0,20.0,200.0]
y = set(int(item) for item in l)
queries = [23,68,103]
ewma = []
x = 1 if (0 in y) else 0
for i in xrange(1, queries[-1]):
x = (1-a)*x
if i in y:
x += a
if i == queries[0]:
ewma.append(x)
queries.pop(0)
When it's done, ewma should have the moving averages for each query point.
Edited to include SchighSchagh's improvements.

Categories

Resources