[EDIT3]: Trying to shortly and simply explane why do I need to implement it this way made me re-thing the whole procedure and the new solution came to my mind, simpler and shorter (:D) It doesn't need the Nr_Heap class at all. So, in a way, case dismissed.
[EDIT1]: I've edited the question, maybe it will be more understandable now.
I'm implementing an algorithm that is based on some computational geometry procedures. The input is a polyline build of points:
class Point:
def __init__(self, nr=0, x=0, y=0):
self._x = x
self._y = y
self._nr = nr
self._hierarchy = None
self._is_left = 0 #0 - pt is on line
#1 - pt is on the left side of the line
#-1 - pt is on the right side of the line
self._father = None
self._mother = None
self._nr_max = 0
[.........]
The process starts from preparing Regions consisting of consecutive polyline points that are on the same (left or right) side of a line connecting Start and End polyline points (aka SE Line).
Preparing the Regions:
For each point of processed polyline count if its on the left or right side of SE Line,
Along the way prepare an intersection_list of points where a polyline's segment intersects SE Line (i.e. is_left property changes from left to right, or the other way around), marking start and end points of such segment,
Catch points on the left of SE Line and put them on an auxiliary Nr_Heap queue,
Divide points on this Nr_Heap into Regions (that is make a new region when points on the Nr_Heap are no longer consecutive),
Repeat 3. and 4. for the points on the right side of SE Line.
Nr_Heap is planned to be a collections.deque() data structure, like below. I'm omitting unrelevant functions in the code.
class Nr_Heap:
"""
Deque of points, auxiliary to defying a Region_List
"""
def __init__(self):
self._top = None
self._tmp_bottom = None
#property
def top(self):
return self._top
#top.setter
def top(self, queue):
self._top = queue #queue: collections.deque()
def pop(self):
if self.top:
try:
self.top.popleft()
except IndexError:
#raise IndexError(f"The queue is empty, nothing to pop.")
return None
else:
raise ValueError(f"Nr_Heap queue not initiated, no queue to push point into.")
return self.top
I need to implement a Nr_Heap clean function, that pops items, starting from the top, but only while they meet two criteria (see 4. above). Those are:
(1) next item to the current one exists and
(2) the absolute value of difference of those two (current and next) items ._nr (see Points class) values equals 1.
Those popped points are added to a Region structure. When no such items exists the popping stops, what means that after such cleaning there can still be more points in the Nr_Heap, waiting for new Region to be started.
I tried like this:
def clean(self):
if self.top:
p = self.top
it = iter(self.top)
try:
n = next(it)
n = next(it)
except StopIteration as e:
n = None
while n and abs(p[0].nr - n.nr) == 1:
p = self.pop()
try:
n = next(it)
except StopIteration as e:
n = None
return self.top
I get a RuntimeError: deque mutated during iteration (not a surprise, if I'm popping an item...), but I have no other idea how to do it.
So my question is: using collections.deque() - how to peek into next item and check its properties to have conditions for a "while" loop? Is it possible to peek into next item of a deque() at all? Or maybe into a list, or some other python's data structure that I could use instead? I could implement my own heap structure, of course, but I wanted to try with the Python's build ones.
[EDIT2]: I got enlighted (sort of ;)) and now it is doing what it is supposed to do (see the code below). But if someone could do it better or answer my question above I'd be most grateful.
def clean(self):
while self.top:
it = iter(self.top)
try:
n = next(it)
n = next(it)
except StopIteration as e:
n = None
if n and abs(self.top[0].nr - n.nr) == 1:
self.pop()
else:
break
return self.top
I am using python and mpi4py, and have encountered a scenario I do not understand. The below code is a minimal working example mwe.py.
import numpy as np
from mpi4py import MPI
import time
import itertools
N=8
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
N_sub = comm.Get_size()-1
get_sub = itertools.cycle(range(1, N_slaves+1))
if rank == 0:
print("I am rank {}".format(rank))
data = []
for i in range(N):
nums = np.random.normal(size=6)
data.append(nums)
sleep_short = 0.0001
sleep_long = 0.1
sleep_dict = {}
for r in range(1, N_sub+1):
sleep_dict[str(r)] = 1
data_idx = 0
while len(data) > data_idx:
r = next(get_sub)
if comm.iprobe(r):
useless_info = comm.recv(source=r)
print(useless_info)
comm.send(data[data_idx], dest=r)
data_idx += 1
print("data_idx {}".format(data_idx))
sleep_dict[str(r)] = 1
else:
sleep_dict[str(r)] = 0
if all(value == 0 for value in sleep_dict.values()):
time.sleep(sleep_long)
else:
time.sleep(sleep_short)
for r in range(1, N_sub+1):
comm.send('Done', dest=r)
else:
print("rank {}".format(rank))
######################
# vvv This is the statement in question
######################
comm.Barrier()
while True:
comm.send("I am done", dest=0)
model = comm.recv(source=0)
if type(model).__name__ == 'str':
break
MPI.Finalize()
sys.exit(0)
When run with mpirun -np 4 python mwe.py, this code generates an array containing lists of random numbers, and then distributes these lists to the "sub" ranks until all arrays have been sent. Understandably, if I insert a comm.Barrier() call near the bottom (where I have indicated in the code), the code no longer completes execution, as the sub ranks (not equal to 0) never get to the statement where they are to receive what is being sent from rank 0. Rank 0 keeps trying to find a rank to pass the array to, but never does since the other ranks are held up, and the code hangs.
This makes sense to me. What doesn't make sense to me is that with the comm.Barrier() statement included, the preceding print statement also does not execute. Based on my understanding, the sub ranks should proceed normally until they hit the barrier statement, and then wait there until all of the ranks 'catch up', which in this case never happens because rank 0 is in its own loop. If this is the case, the preceding print statement should be executed, as it comes before those ranks have gotten to the barrier line. So why does the statement not get printed? Can anyone explain where my understanding fails?
I have a question in regard to time complexity (big-O) in Python. I want to understand the general method I would need to implement when trying to find the big-O of a complex algorithm. I have understood the reasoning behind calculating the time complexity of simple algorithms, such as a for loop iterating over a list of n elements having a O(n), or having two nested for loops each iterating over 2 lists of n elements each having a big-O of n**2. But, for more complex algorithms that implement multiple if-elif-else statements coupled with for loops, I would want to see if there is a strategy to, simply based on the code, in an iterative fashion, to determine the big-O of my code using simple heuristics (such as, ignoring constant time complexity if statements or always squaring the n upon going over a for loop, or doing something specific when encountering an else statement).
I have created a battleship game, for which I would like to find the time complexity, using such an aforementioned strategy.
from random import randint
class Battle:
def __init__(self):
self.my_grid = [[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False]]
def putting_ship(self,x,y):
breaker = False
while breaker == False:
r1=x
r2=y
element = self.my_grid[r1][r2]
if element == True:
continue
else:
self.my_grid[r1][r2] = True
break
def printing_grid(self):
return self.my_grid
def striking(self,r1,r2):
element = self.my_grid[r1][r2]
if element == True:
print("STRIKE!")
self.my_grid[r1][r2] = False
return True
elif element == False:
print("Miss")
return False
def game():
battle_1 = Battle()
battle_2 = Battle()
score_player1 = 0
score_player2 = 0
turns = 5
counter_ships = 2
while True:
input_x_player_1 = input("give x coordinate for the ship, player 1\n")
input_y_player_1 = input("give y coordinate for the ship, player 1\n")
battle_1.putting_ship(int(input_x_player_1),int(input_y_player_1))
input_x_player_2 = randint(0,9)
input_y_player_2 = randint(0,9)
battle_2.putting_ship(int(input_x_player_2),int(input_y_player_2))
counter_ships -= 1
if counter_ships == 0:
break
while True:
input_x_player_1 = input("give x coordinate for the ship\n")
input_y_player_1 = input("give y coordinate for the ship\n")
my_var = battle_1.striking(int(input_x_player_1),int(input_y_player_1))
if my_var == True:
score_player1 += 1
print(score_player1)
input_x_player_2 = randint(0,9)
input_y_player_2 = randint(0,9)
my_var_2 = battle_2.striking(int(input_x_player_2),int(input_y_player_2))
if my_var_2 == True:
score_player2 += 1
print(score_player2)
counter_ships -= 1
if counter_ships == 0:
break
print("the score for player 1 is",score_player1)
print("the score for player 2 is",score_player2)
print(game())
If it's just nested for loops and if/else statements, you can take the approach ibonyun has suggested - assume all if/else cases are covered and look at the deepest loops (being aware that some operations like sorting, or copying an array, might hide loops of their own.)
However, your code also has while loops. In this particular example it's not too hard to replace them with fors, but for code containing nontrivial whiles there is no general strategy that will always give you the complexity - this is a consequence of the halting problem.
For example:
def collatz(n):
n = int(abs(n))
steps = 0
while n != 1:
if n%2 == 1:
n=3*n+1
else:
n=n//2
steps += 1
print(n)
print("Finished in",steps,"steps!")
So far nobody has been able to prove that this will even finish for all n, let alone shown an upper bound to the run-time.
Side note: instead of the screen-breaking
self.my_grid = [[False,False,...False],[False,False,...,False],...,[False,False,...False]]
consider something like:
grid_size = 10
self.my_grid = [[False for i in range(grid_size)] for j in range(grid_size)]
which is easier to read and check.
Empirical:
You could do some time trials while increasing n (so maybe increasing the board size?) and plot the resulting data. You could tell by the curve/slope of the line what the time complexity is.
Theoretical:
Parse the script and keep track of the biggest O() you find for any given line or function call. Any sorting operations will give you nlogn. A for loop inside a for loop will give you n^2 (assuming their both iterating over the input data), etc. Time complexity is about the broad strokes. O(n) and O(n*3) are both linear time, and that's what really matters. I don't think you need to worry about the minutia of all your if-elif-else logic. Maybe just focus on worst case scenario?
Need to remove every third element and return it once the list has only two elements left
def duck_duck_goose(lst: List[str]) -> List[str]:
"""Given an list of names (strings), play 'duck duck goose' with it,
knocking out every third name (wrapping around) until only two names are
left. In other words, when you hit the end of the list, wrap around and keep
counting from where you were.
For example, if given this list ['Nathan', 'Sasha', 'Sara', 'Jennie'], you'd
first knock out Sara. Then first 'duck' on Jennie, wrap around to 'duck' on
Nathan and 'goose' on Sasha - knocking him out and leaving only Nathan and
Jennie.
You may assume the list has 3+ names to start
Args:
lst - a list of names (strings)
Returns:
the resulting list after playing duck duck goose
"""
raise NotImplementedError("duck_duck_goose")
names = ["sasha", "nathan", "jennie", "shane", "will", "sara"]
assert duck_duck_goose(names) == ["sasha", "will"]
Here's a short snippet on how you can do it,
names = ["sasha", "nathan", "jennie", "shane", "will", "sara"]
def removeThirdName(names):
pos = 3 - 1
index = 0
len_list = (len(names))
while len_list > 2:
index = (pos + index) % len_list
print(names.pop(index))
len_list -= 1
removeThirdName(names)
print(names)
Outputs:
['sasha', 'will']
I need to write a function called flip_coins(n) that returns the amount of heads. Then I need to to write a program that flips 8 coins a thousand times and returns the amount of times heads comes up three times in a row with a loop. After that I have some calculations on probability of the flips. So far this is what I have, but it wont run. What am I missing?
import random
def flip_coin():
return random.choice("HT")
def flip_coins(n):
for i in range(n):
if flip_coins() == "H":
heads_count +=1
return heads_count
for i in range (1000):
if flip_coins(8) == "3":
multiple_heads_count += 1
return multiple_heads_count
print(multiple_heads_probability)
print(second_probability)
multiple_heads_probability = multiple_heads_count/1000
second_probability = 56/256
This is rather interesting, similar to a dice rolling program I wrote a few months ago to get me back into python!
The first thing I noticed is that the code is trying to print two variables before they are created, you also need to be careful when dividing an integer, it will always return an integer, this meant that without converting to a float first (using float() or adding .0 to the end of an integer) the result will always be zero. So I changed those lines to:
multiple_heads_probability = float(multiple_heads_count)/1000
second_probability = 56.0/256
print(multiple_heads_probability)
print(second_probability)
Secondly, if you want the program to find the probability of throwing a heads three times in a row, then I would suggest adding an else statement after your if flip_coin() == "H": block. Then another if statement to determine whether there have been 3 in a row. Also, in order to add to a variable, you must first initialize them, you can do this using: heads_count = 0 and adding to it later. So I changed your second function to:
def flip_coins(n):
heads_count = 0
heads_rows = 0
for i in range(n):
if flip_coin() == "H":
heads_count +=1
else:
heads_count == 0
if heads_count == 3:
heads_rows += 1
heads_count = 0
else:
pass
return heads_rows
Lastly, the for loop. Why are you using return outside of a function? This is only for use when writing a fruitful (not void) function and not in loops (unless inside a fruitful function). In the line if flip_coins(8) == "3":, you are comparing an integer to a string so it will always return false. Because of the changes to flip_coins() I have also changed the for loop in other ways, it is now:
multiple_heads_count = 0
for i in range (1000):
if flip_coins(8) >= 1: #if there were any occurrences of 3 heads (H) in a row
multiple_heads_count += 1
The corrected and complete code is below, along with 10 possible outputs (out of thousands).
Code:
import random
def flip_coin():
return random.choice("HT")
def flip_coins(n):
# initialize two variables for incrementation
heads_count = 0
heads_rows = 0
for i in range(n): # do n times
if flip_coin() == "H": # if flipped a heads
heads_count +=1 # increment heads_count by one
else: # otherwise
heads_count == 0 # reset heads_count
if heads_count == 3: # if there have been three heads so far
heads_rows += 1 # increment heads_rows by one
heads_count = 0 # reset heads_count
else: # otherwise
pass # do nothing
return heads_rows
multiple_heads_count = 0 # initialize a variable for incrementation
for i in range (1000): # do 1000 times
if flip_coins(8) >= 1: # if there was at least one three heads in a row
multiple_heads_count += 1 # increment multiple_heads_count by one
multiple_heads_probability = float(multiple_heads_count)/1000 # calculate the probability of having at least one heads in a row
second_probability = 56.0/256 # This is the supposed theoretical probability, however I have not checked this yet
print(multiple_heads_probability)
print(second_probability)
Outputs:
0.865
0.21875
0.851
0.21875
0.853
0.21875
0.861
0.21875
0.866
0.21875
0.89
0.21875
0.839
0.21875
0.856
0.21875
0.855
0.21875
0.841
0.21875
I am intrigued as to what 56.0/256 represents, could someone enlighten me in a comment?
It looks like you are calling "flip_coins" from inside itself. You want to call "flip_coin" from inside "flip_coins" like this:
def flip_coins(n):
for i in range(n):
if flip_coin() == "H": ## changed to flip_coin
heads_count +=1
return heads_count
EDIT: There are several more issues here.
Variables must be assigned (i.e. appear on the left-hand side of an = sign) before they are referenced (appear in an expression to be evaluated). This occurs in the function "flip_coins" where you need to assign heads_count=0 before you start you loop. Also multiple_heads_count is never initialized before you try to increment it in the main (the code not inside a function definition). Finally, you are trying to print two variables, multiple_heads_probability and second_probability before you ever assign them.
You are incorrectly trying to compare an integer with a string in your loop
for i in range (1000):
if flip_coin(8) == "3": ## changed to flip_coin()
multiple_heads_count += 1
The value of flip_coin(8) is an integer, but you are checking for equality with the string "3". Strings and integers are never equal. In this case, just remove the quotes from around the 3.
You have a return statement outside of a function
return multiple_heads_count
This statement can be deleted. After running the loop, the value of multiple_heads_count will still be available, because you are still in the same scope, so you can just print it out or use it however you need.
(EDIT: NOTE - this paragraph applies to Python 2.x, but not Python 3.x) Once you fix those syntax errors, you're going to find that your results always print "0". That's because in python, division of two integers produces an integer result (the quotient, with the remainder thrown away). In this case, you're always dividing an integer by a larger integer, so the quotient is always zero. What you want is to divide the integers and get a floating point result, so you can force python to do this by specifying at least one element of the quotient as a floating point:
multiple_heads_probability = multiple_heads_count/1000.0 ## added ".0"
You can add ".0" to any integer value, and it will force floating point division. This is a very important feature of python to be aware of.
Finally, based on your problem statement, your logic is wrong. Your problem states that you should count instances of "three heads in a row" out of 8 coin flips, but your code counts ALL heads out of the 8, and returns the total number of heads. Now, there is some ambiguity here. You are checking for EXACTLY three heads, but is that what the problem states? Even if you fix the logic to look for consecutive heads, you will ignore "HHHH", "HHHHH", etc. because you are testing for exactly three. Also, are you supposed to count "HHHTTHHH" as two instances of three heads in a row?
Python is a great language, and it is a great choice for this kind of problem. Good luck!