How to conceptually implement brute-force / tree traversal? - python

As a beginner programmer, I don't know how to conceptually think about brute-forcing. My mind can't really fathom how to write code that will try every possibility. My answer is recursion, but as you will see below, I have failed.
If you need the full source
I have a problem that I want to solve. Here is a code snippet (there are other functions, but no reason to include them here, they just do background work):
def initiate(seen):
step_segment = []
STATS = [250,0,0,0,13,0]
if len(seen) >= 256: # when to escape the recursion
return seen
while (len(step_segment)) < 128:
step_segment, STATS = take_step(step_segment, STATS)
if STATS[5] == "B": # if there is a battle
if STATS[0] in seen: # if battle has been done before
status = seen.index(STATS[0]) # get battle status:
status += 1 # which is next to step_id (= G or B)
if seen[status] == "B": # for seen battles, try Glitch ("G")
step_segment = do_glitch(step_segment, STATS)
else:
step_segment, STATS = do_fight(step_segment, STATS) # fight
seen = seen + [STATS[0],STATS[5]]
time = get_frames(step_segment)
print "\nTime:", time
print seen
return initiate(seen)
The goal: I want to produce a list of every possible decision through a segment, along with how long it takes.
Description:
I will take a step (There's only one direction: forward). Every time I take a step, my stats are updated. This takes a step and updates the stats: step_segment, STATS = take_step(step_segment, STATS)
A list of steps taken, along with the stats, are kept in
step_segment. This is so I can 'undo' an arbitrary amount of
steps, if I want. To undo a step call the function:
step_segment, STATS = undo_step(step_segment, STATS)
I can see how long my current route has taken by doing: time = frames(step_segment).
At some point, I will get into a Battle. I get into a battle when
STATS[5] == "B"
When there is a battle I have to make a decision, I simply have two choices: i. Fight the
battle (B), or, ii. Run away glitch (G).
If I want to Fight, I do: step_segment = do_fight(step_segment, STATS). This also records that I chose to fight, along with the stats, in step_segment. (So I can undo it, if i want).
If I want to Run Away Glitch, I do: step_segment = do_glitch(step_segment,STATS).
I want to see every possible combination of Glitch & Fight (the only two choices, when I reach a battle).
At the moment, my code is very bad and does not try all of the possibilities. I don't really know how to code for all possibilities.
So, that's why I'm here. How can I implement a way of trying all possibilities when facing a decision?
I understand the problem has exponential amount of possibilities, but thankfully the maximum number is pretty small (<1024).
I have read about tree traversal, but I have no idea how my problem can be put into that format. Eg, I don't know what a 'node' would be in my problem. I actually don't know what anything would be... That's why I'm asking.

Related

How do I fix infinite loop bugs in Python?

I have a problem regarding a competition question I'm attempting to do. Here is the question (its a bit long)
""""
Welcome aboard, Captain! Today you are in charge of the first ever doughnut-shaped spaceship, The
Circular. There are N cabins arranged in a circle on the spaceship. They are numbered from 1 to N in
a clockwise direction around the ship. The ith and the (i + 1)th cabins are connected. So too are cabin
1 and cabin N.
Currently the ith cabin has Ai crewmates, however the spaceship cannot depart unless there are exactly
Bi crewmates in this cabin.
To achieve this, you have the power to pay crewmates to change cabins. You can pay a crewmate $1 to
move to an adjacent cabin. A crewmate can be asked to move multiple times, provided that you pay
them $1 each time.
What is the fewest dollars you must pay before you can depart? It is always be possible to depart.
""""
https://orac2.info/problem/aio22spaceship/ (the link to the intereactive Qs)
I searched the web and i found no solutions to the Q. My code seems to be infinite looping i guess but im not sure as i cant see what cases the sit uses to determine if my code is right.
Heres my code
#!/usr/bin/env python
import sys
sys.setrecursionlimit(1000000000)
#
# Solution Template for Spaceship Shuffle
#
# Australian Informatics Olympiad 2022
#
# This file is provided to assist with reading and writing of the input
# files for the problem. You may modify this file however you wish, or
# you may choose not to use this file at all.
#
# N is the number of cabins.
N = None
# A contains the initial number of crewmates in each cabin. Note that here the
# cabins are numbered starting from 0.
A = []
# B contains the desired number of crewmates in each cabin. Note that here the
# cabins are numbered starting from 0.
B = []
answer = 0
# Open the input and output files.
input_file = open("spacein.txt", "r")
output_file = open("spaceout.txt", "w")
# Read the value of N.
N = int(input_file.readline().strip())
# Read the values of A and B.
input_line = input_file.readline().strip()
A = list(map(int, input_line.split()))
input_line = input_file.readline().strip()
B = list(map(int, input_line.split()))
AM = A
#AM is my modifying set
# TODO: This is where you should compute your solution. Store the fewest
# dollars you must pay before you can depart into the variable
while AM != B:
#Check if the set is correct
#notfound is a testing variable to see if my code was looping due to input error
notfound = True
for i in range(N):
#Check which places needs people to be moved
while AM[i]>B[i]:
notfound = False
#RV and LV check the "neediness" for each half's people requirements. I check how many people
#are needed on one side compared to the other and subtract the "overflow of people"
RV = 0
LV = 0
for j in range(int(N/2-0.5)):
#The range thing makes sure that if N is odd, im splitting the middle but if N is even, i leave out the end pod
RV += B[(i+j+1)%N]-AM[(i+j+1)%N]
LV += B[(i-j-1)%N]-AM[(i-j-1)%N]
answer +=1
if RV>LV:
AM[i]+=-1
AM[(i+1)%N]+=1
else:
AM[i]+=-1
AM[(i-1)%N]+=1
print(AM,B)
if notfound:
break
print(answer)
# Write the answer to the output file.
output_file.write("%d\n" % (answer))
# Finally, close the input/output files.
input_file.close()
output_file.close()
please help i really neeed to know the answer, driving me mad ngl
Welp, there aren't any resources online and I've tried everything. I think the problem might be that because of my solving method, passengers may be flicked between two pods indefinitely. Not sure since i could make a case that demoed this.
also my post probably is messy since this is my first time posting

Modifying a dictionary inside of an if statement

I've been trying to make a basic text game in Python, and I'm using dictionaries to contain the player's information. I want to make it so that when a player's health reaches 0, the code will stop running. I've had trouble making that happen. My dictionary, which is at the beginning of my code, looks like this:
playerAtt = {}
playerAtt["Weapon"] = "Baseball bat"
playerAtt["Party"] = "Empty"
playerAtt["Health"] = 15
playerAtt["Cash"] = "$100"
print(playerAtt)
if playerAtt['Health'] <= 0:
exit()
The bottom section is what I wrote to try and make the code stop running when the player's health reached zero, but it doesn't seem to work. In one path of my game, your health gets set to zero and the game is supposed to end, but the program continues to run:
townChoice = raw_input("You met a traveler in the town. 'Yo. I'm Bob. Let's be friends.' Will you invite him to your party? Y/N\n")
if townChoice == 'y' or townChoice == 'Y':
print("That kind traveler was not such a kind traveler. He stabbed you with a machete. RIP " + Name + '.')
playerAtt['Health'] == 0
When you reach this part of the game, all it does is print the message, and moves on to the next decision. In this situation, I could just manually end the program by doing exit() under the print command, but there are circumstances where the player only loses a fraction of their health, and they would eventually reach zero. Sorry if this is a stupid question, I've only been working on Python for a few days.
You have two "=" when you set the player's health to 0
I had put 2 == instead of 1 = when defining
playerAtt["Health"].
Also, I needed to make sure it was constantly checking if the player's health was zero, so I used a while loop. I used
while playerAtt["Health"] = 0:
deathmsg()
exit()
to fix it. deathMsg was a function I made to display a random death message, for more information.

Why does this function not return a value?

Be forewarned, I just started learning Python, and its my first time on this site. If I act like a n00b, please don't hate.
So I have created a program that is supposed to tell you how long it will take you to get to a star (distance is specified) at the speed of light, and factors of the speed of light. It begins with a library called easygui, which creates a nice window, which the user chooses a factor. The factor they have chosen becomes the variable "choice". This section of the code works fine. Ideally, this value would then be fed into a function, which would do the factoring, and return a value for the number of days of travel. This is unsuccessful. Most likely, I have simply set this up wrong, so if anyone knows the proper way to use functions, I'd really appreciate your help! Oh, and I tried to comment like crazy, so hopefully everything makes sense!
import easygui as eg #the gui creation library I am using
dist = 41000000000000 #distance to the star
light = 300000 #speed of light
def Convert (factor): #takes in factor chosen by user
speed = light*factor #the speed is the factor multiplied by the speed of light
time = (dist/speed)/3600 # the time is the distance/divided by the speed, since thats a huge value in seconds, the /3600 should reduce it to days
return time #"should" return the value it got for "time"
msg = "Choose a warp factor:" #creates a gui window for user to select factor
title = "Warp Factor Selection"
choices = ["1", "3", "5", "10", "50", "100", "200", "500", "1000"]
choice = eg.buttonbox(msg, title, choices) #gui returns the user's selection as "choice" WORKS!
choice = float(choice) #changes choice to float
if choice == 1:
Convert(choice) #attempts to feed "choice" into the function "convert" DOES NOT WORK :(
print (Convert(1)) #then print the value created from convert (have also tried print(time) but it always returns 0)
At this point in time, it is intentionally set up to only accept the choice of 1 as the factor. I want to figure this function thing out before I go and do the rest of the possible factors
thefourtheye already explained why, but if you want to avoid this in the future you could switch to Python 3 division by putting this at the top of your file:
from __future__ import division
In Python 3, it behaves more intuitively in situations like this (1/2 == .5) while you can still get the integer division behavior with // (1//2 == 0)
When you do
(dist/speed)/3600
if the (dist/speed) is lesser than 3600, result will be 0. You can try that out yourself,
print 3599/3600
will print
0
So, you need to convert the data to float like this
def Convert (factor):
speed = light*factor
return (float(dist)/float(speed))/3600.0
You may want to do this
if str(choice) in choices:
Convert(choice)
print (Convert(choice))
That way, you don't have to make a new if condition to test each number. This just says that if choice is in choices list, execute the function with choice.

Python recursion depth exceeded limit exceeded, and don't know how to delete recursion

Maybe the problem can be solved by deleting all those functions, can't it?
However, i really don't know what to do to get the source work.
By the way, it just simulates a horse in a chesstable, going around and around, randomly, trying to visit each square once; and I get a recursion depth exceeded error.
import random
def main():
global tries,moves
tries,moves=0,0
restart()
def restart():
global a,indexes,x,y
a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
indexes=[x for x in range(8)]
#Random part
x=random.randint(0,7)
y=random.randint(0,7)
a[x][y]=1
start()
def start():
global i,indexes,moves,tries
i=0
random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
while i<=7:
if indexes[i]==0:
move(-2,-1)
elif indexes[i]==1:
move(-2,1)
elif indexes[i]==2:
move(-1,-2)
elif indexes[i]==3:
move(-1,2)
elif indexes[i]==4:
move(1,-2)
elif indexes[i]==5:
move(1,2)
elif indexes[i]==6:
move(2,-1)
elif indexes[i]==7:
move(2,1)
i+=1
for _ in a:
if 0 in _:
print "Wasted moves: %d"%(moves)
tries+=1
moves=0
restart()
print "Success obtained in %d tries"%(tries)
def move(column,row):
global x,y,a,moves
try: b=a[x+row][y+column]
except IndexError: return 0
if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
x=x+row
y=y+column
a[x][y]=1
moves+=1
start()
else: return 0
try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors
EDIT: This is the modified version, which still does not works, but at least it's an improvement:
import random
def move((column,row),x,y):
try: cell=squares_visited[x+row][y+column]
except IndexError: return x,y ## NONE TYPE OBJECT
if cell==0 and 0<=x+row<=7 and 0<=y+column<=7:
x+=row
y+=column
squares_visited[x][y]=1
return x,y ## NONE TYPE OBJECT
squares_visited=[[0] * 8 for _ in range(8)]
x=random.randint(0,7)
y=random.randint(0,7)
squares_visited[x][y]=1
moves=[(-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)]
indexes=list(range(8))
tries=0
print "The horse starts in position %d,%d"%(x,y)
while True:
random.shuffle(indexes)
for _ in indexes:
cells=move(moves[indexes[_]],x,y) ##Passing as arguments x and y looks weird
x=cells[0]
y=cells[1]
#If you out this for cicle, there are no legal moves anymore(due to full completion with 1, or to lack of legit moves)
for _ in squares_visited:
if 0 in _:
squares_visited=[[0] * 8 for _ in range(8)]
tries+=1
else:
print "You managed to do it in %d tries."%(tries)
This code has a lot of problems -- enough that it's worth going over in full:
import random
def main():
global tries,moves
The first of many examples of over-use of global variables. When possible, pass parameters; or create a class. This is a general strategy that will help you construct more comprehensible (and thus more debuggable) algorithms; and in a general sense, this is part of why your code fails -- not because of any particular bug, but because the complexity of your code makes it hard to find bugs.
tries,moves=0,0
restart()
def restart():
global a,indexes,x,y
Why do you name your board a? That's a terrible name! Use something descriptive like squares_visited.
a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
indexes=[x for x in range(8)]
Minor: in python 2, [x for x in range(8)] == range(8) -- they do exactly the same thing, so the list comprehension is unnecessary. In 3, it works a little differently, but if you want a list (rather than a range object) just pass it to list as in (list(range(8))).
#Random part
x=random.randint(0,7)
y=random.randint(0,7)
a[x][y]=1
start()
So my understanding of the code so far is that a is the board, x and y are the starting coordinates, and you've marked the first spot visited with a 1. So far so good. But then things start to get hairy, because you call start at the end of restart instead of calling it from a top-level control function. That's theoretically OK, but it makes the recursion more complicated than necessary; this is another part of your problem.
def start():
global i,indexes,moves,tries
Argh more globals...
i=0
random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
while i<=7:
if indexes[i]==0:
move(-2,-1)
elif indexes[i]==1:
move(-2,1)
elif indexes[i]==2:
move(-1,-2)
elif indexes[i]==3:
move(-1,2)
elif indexes[i]==4:
move(1,-2)
elif indexes[i]==5:
move(1,2)
elif indexes[i]==6:
move(2,-1)
elif indexes[i]==7:
move(2,1)
i+=1
Ok, so what you're trying to do is go through each index in indexes in sequence. Why are you using while though? And why is i global?? I don't see it being used anywhere else. This is way overcomplicated. Just use a for loop to iterate over indexes directly, as in
for index in indexes:
if index==0:
...
Ok, now for the specific problems...
for _ in a:
if 0 in _:
print "Wasted moves: %d"%(moves)
tries+=1
moves=0
restart()
print "Success obtained in %d tries"%(tries)
I don't understand what you're trying to do here. It seems like you're calling restart every time you find a 0 (i.e. an unvisited spot) on your board. But restart resets all board values to 0, so unless there's some other way to fill the board with 1s before hitting this point, this will result in an infinite recursion. In fact, the mutual recursion between move and start might be able to achieve that in principle, but as it is, it's way too complex! The problem is that there's no clear recursion termination condition.
def move(column,row):
global x,y,a,moves
try: b=a[x+row][y+column]
except IndexError: return 0
if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
x=x+row
y=y+column
a[x][y]=1
moves+=1
start()
else: return 0
Here, in principle, the idea seems to be that if your move hits a 1 or goes off the board, then the current branch of the recursion terminates. But because i and indexes are global above in start, when start is re-called, it re-shuffles indexes and resets i to 0! The result is sheer chaos! I can't even begin to comprehend how that will effect the recursion; it seems likely that because i gets reset at the beginning of start every time, and because every successful call of move results in a call of start, the while loop in start will never terminate!
I suppose it's possible that eventually this process will manage to visit every square, at which point things might work as expected, but as it is, this is too complex even to predict.
try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors
Not sure what you mean by that last line, but it doesn't sound like a good sign -- you're papering over an error instead of finding the root cause.
I'm going to play with this code a bit and see if I can get it to behave marginally better by de-globalizing some of its state... will report back soon.
Update:
Ok I de-globalized indexes as described above. I then replaced the start/restart recursion with an infinite loop in restart, a return statement in start where the call to restart used to be, and a sys.exit() at the end of start (to break out of the infinite loop on success). The result behaves more as expected. This is still poor design but it works now, in the sense that it recursively tries a bunch of random paths until every local position has been visited.
Of course it still doesn't ever succeed; it just keeps looping. Actually finding a solution will probably require a lot more rethinking of this algorithm! But following my above suggestions should help some, at least.
start() and move() call each other, making it an indirect recursive call BUT the move() return startment get out of move() and notout of the recursion.
You see, when you are calling a function, that calls a function that calls a functions, it all goes in a stack that reference each calls. When you get a your final result, you are supposed to go backward, and unstack these function calls.
Here, you don't, you call move(), that calls start(), and it it returns something then you just keep going deeper in the stack.
Try to make an iterative version of your code. Recursion is hard, start with something easier.
If you do want to persist in the recursive version, make move() call itself, and then go backward in the stack from it self once it reach the out condition. It will be clearer than dealing with recursive calls from two functions.
BTW:
Avoid using global variables. Either pass the data as arguments, or use a class. I would use argument passing, it will force you to come up with a better algo that this one.
the while loop is not necessary. Replace it with a for loop on indexes
the huge if/elif statement is not necessary, replace it with a dictionary
You should end up with somthing like this:
for i in indexes:
move(*MOVES[i])
MOVES, being a dict of values of i associated with params for move().
you may want to use generators instead of your list comprehensions, but that would require some algo changes. It could be better for your memory footprint. At the very least, make this shorter:
[x for x in range(8)] can be written range(8)
[[0 for y in range(8)] for x in range(8)] can be written [[0] * 8 for x in range(8)]
range() can be replaced by xrange()

Infinite loop in simulation

I'm starting out in python.. The details I have written in the below.. It goes to an infinite loop and give me an error when I try to call the function inside itself.. Is this kind of recursion not allowed ?
Posting code below.. Thanks for all your help :)
The program assumes that we have 100 passengers boarding a plane. Assuming if the first one has lost his boarding pass, he finds a random seat and sits there. Then the other incoming passengers sit in their places if unoccupied or some other random seat if occupied.
The final aim is to find the probability with which the last passenger will not sit in his/her own seat. I haven't added the loop part yet which
would make it a proper simulation. The question above is actually a puzzle in probability. I am trying to verify the answer as I don't really follow the reasoning.
import random
from numpy import zeros
rand = zeros((100,3))
# The rows are : Passenger number , The seat he is occupying and if his designated seat is occupied. I am assuming that the passengers have seats which are same as the order in which they enter. so the 1st passenger enter has a designated seat number 1, 2nd to enter has no. 2 etc.
def cio(r): # Says if the seat is occupied ( 1 if occupied, 0 if not)
if rand[r][2]==1:
return 1
if rand[r][2]==0:
return 0
def assign(ini,mov): # The first is passenger no. and the second is the final seat he gets. So I keep on chaning the mov variable if the seat that he randomly picked was occupied too.
if cio(rand[mov][2])== 0 :
rand[mov][2] = 1
rand[mov][1] = ini
elif cio(rand[mov][2])== 1 :
mov2 = random.randint(0,99)
# print(mov2) Was used to debug.. didn't really help
assign(ini,mov2) # I get the error pointing to this line :(
# Defining the first passenger's stats.
rand[0][0] = 1
rand[0][1] = random.randint(1,100)
m = rand[0][1]
rand[m][2]= 1
for x in range(99):
rand[x+1][0] = x + 2
for x in range(99):
assign(x+1,x+1)
if rand[99][0]==rand[99][1] :
print(1);
else :
print(0);
Please tell me if y'all get the same error.. ALso tell me if I am breaking any rules coz thisi sthe first question I'm posting.. Sorry if it seems too long.
This is how it should've been...
The code does work fine in this case with the following mods :
def assign(ini,mov):
if cio(mov)== 0 : """Changed here"""
rand[mov][2] = 1
rand[mov][1] = ini
elif cio(mov)== 1 : """And here"""
mov2 = random.randint(0,99)
assign(ini,mov2)
I am using Python 2.6.6 on Windows 7, using a software from Enthought Academic Version of Python.
http://www.enthought.com/products/getepd.php
Also the answer to this puzzle is 0.5 which is actually what I am getting(almost) by running it 10000 times.
I didn't see it here but it had to be available online..
http://www.brightbubble.net/2010/07/10/100-passengers-and-plane-seats/
Recursion, while allowed, isn't your best first choice for this.
Python enforces an upper bound on recursive functions. It appears that your loop exceeds the upper bound.
You really want some kind of while loop in assign.
def assign(ini,mov):
"""The first is passenger no. and the second is the final seat he gets. So I keep on chaning the mov variable if the seat that he randomly picked was occupied too.
"""
while cio(rand[mov][2])== 1:
mov = random.randint(0,99)
assert cio(rand[mov][2])== 0
rand[mov][2] = 1
rand[mov][1] = ini
This may be more what you're trying to do.
Note the change to your comments. Triple-quoted string just after the def.
you may be able to find the exact solution using dynamic programming
http://en.wikipedia.org/wiki/Dynamic_programming
For this you will need to add memoization to your recursive function:
What is memoization and how can I use it in Python?
If you just want to estimate the probability using simulation with random numbers then I suggest you break out of your recursive function after a certain depth when the probability is getting really small because this will only change some of the smaller decimal places (most likely.. you may want to plot the change in result as you change the depth).
to measure the depth you could add an integer to your parameters:
f(depth):
if depth>10:
return something
else: f(depth+1)
the maximum recursion depth allowed by default is 1000 although you can change this you will just run out of memory before you get your answer

Categories

Resources