Incrementing in a for loop - python

So my issue is with this not incrementing correctly... I tried to uses an int "step" to + 1 every time this loop is ran but it doesn't do anything. Why is that? Also when I print(step) it only adds up to 337. It does not go the full 1000 like I had thought I asked it too. How do I do this correctly?
lockers = []
step = 3
locker = 0
while len(lockers) <= 1000:
lockers.append(1)
for i in range(0, len(lockers)):
lockers[i] = 0
for i in range(0, len(lockers), 2):
lockers[i] = 1
for i in range(0, len(lockers), step):
if lockers[i] == 0:
lockers [i] = 1
else:
lockers[i] = 0
step += 1
print(lockers)

range gives you an iterable object:
>>> range(10,20 , 2)
range(10, 20, 2)
>>> list(range(10,20 , 2))
[10, 12, 14, 16, 18]
The values in it are fully decided as soon as the call returns, and aren't re-evaluated each time around the loop. Your step only goes up to 337 because you are incrementing it once for each element in the object range(0, 1000, 3), which has 334 items, not 1000:
>>> len(range(0,1000,3))
334
To get something that works like range but advances the step, you would need to write your own generator:
def advancing_range(start, stop, step):
''' Like range(start, stop, step) except that step is incremented
between each value
'''
while start < stop:
yield start
start += step
step += 1
You can then do for i in advancing_range(0, 1000, 3): and it will work as you intend.
But this is a very strange thing to want to do. Judging by your variable names, I would guess you're coding the locker problem, which says:
A new high school has just been completed. There are 1,000 lockers
in the school and they have been numbered from 1 through 1,000.
During recess (remember this is a fictional problem), the students
decide to try an experiment. When recess is over each student will
walk into the school one at a time. The first student will open all
of the locker doors. The second student will close all of the locker
doors with even numbers. The third student will change all of the
locker doors that are multiples of 3 (change means closing lockers
that are open, and opening lockers that are closed.) The fourth
student will change the position of all locker doors numbered with
multiples of four and so on. After 1,000 students have entered the
school, which locker doors will be open, and why?
But the advancing range logic says something more like "the first student opens the first locker, then the second opens the second locker after that, then the third student opens the third locker after that ...". You want to affect multiple lockers each time, but further spaced out. Essentially, you want to copy and paste your first two loops another 998 times with a one higher step each time. Of course, you can do better than copy and paste, and this seems like you want two nested loops, where the outer one advances the step that the inner one uses. That would look like this:
for step in range(1, len(lockers)):
for i in range(step, len(lockers), step):
Simplifying your other logic by using booleans instead of 1 and 0, the whole program looks like this:
lockers = [True] * 1000
for step in range(1, len(lockers)):
for i in range(step, len(lockers), step):
lockers[i] = not lockers[i]
print(sum(lockers))
It prints that the number of open lockers is 969.

If you want to adjust the step size while iterating, you can have an own range object:
class AdjustableRange(object):
def __init__(self, start, stop, step):
self.start = start
self.stop = stop
self.step = step
self.value = None
def __iter__(self):
if self.value is None:
self.value = start
while self.value < self.stop:
yield self.value
self.value += self.step
This (untested) one you can use for iterting like
rg = AdjustableRange(0, len(lockers), step):
for i in rg:
if lockers[i] == 0:
lockers [i] = 1
else:
lockers[i] = 0
rg.step += 1 # this influences the iteration
But, as was already said, there are better ways to solve your "real" problem.

Related

Generators function

MAX_STUDENTS = 50
def get_student_ids():
student_id = 1
while student_id <= MAX_STUDENTS:
# Write your code below
n = yield student_id
if n != None:
student_id = n
continue
student_id += 1
student_id_generator = get_student_ids()
for i in student_id_generator:
# Write your code below
if i == 1:
i = student_id_generator.send(25)
print(i)
Im quite confused, when i run the code below, i understand the the send function gives 25 as the yield value and assigns it to n, however when entering the if statement, checking if n is not None, wouldnt this create an infinite loop, since n is not none, would take us to the continue statement, which takes us back to the next iteration of the while loop, completely skipping the incrementing of student id
The first thing one can notice is that n will always be of type none except when you send 25 into the generator.
Let's look at the flow of the program
this is the driver code at the start of the program
student_id_generator = get_student_ids() #creates a generator class
for i in student_id_generator: #interates through the generator class
if i == 1: # note that the first i == 1, so the first time the code hits
#this condition, it will be true
i = student_id_generator.send(25) #sets n to be 25
print(i) #for the first iteration, will return 1, as the first yield is 1
now, since n=25 (you just sent 25 into the generator code), let's look at the generator
n = yield student_id # n=25
if n != None: #since n==25, n is not none
student_id = n #student_id = 25
continue #go back to the while loop
student_id += 1 # will not be run
Now that student_id = 25, for the next iteration in the driver code, it will yield 25, so 25 will be printed. But n will be None as nothing is sent to the generator, so student_id += 1 will be run. From there, the while loop kicks in and it will continue until 'student_id == 50', where the loop breaks.
Will there be an infinite loop? No. Because the condition 'n != None' only occurs once.
I hope this helps. If you are still confused, my suggestion is to take out a pen and paper and work out what happens to the code step by step. I did that to help myself understand it.

Python 3 Code explanation needed (for loop): Print reverse number from 10 to 1

output:
1
3 2
6 5 4
10 9 8 7
input:
start = 1
stop = 2
current_num = stop
for row in range(2, 6):
for col in range(start, stop):
current_num -= 1
print(current_num, end=' ')
print("")
start = stop
stop += row
current_num = stop
what is the last 3 expressions do and what does each variable represent?
does anyone understand it?
I am so confused
start = stop
stop += row
current_num = stop
Tha last 3 expressions update the new states of the variables, from the current iteration of the row-loop, to the next.
The initial set of variables are described by;
start = 1
stop = 2
current_num = stop
# current_sum = 2
For the next iteration of the row-loop we want to start counting backwards from where we last stopped + 1. (we have printed "1 ", now we want to print "3 2 ").
start = stop
# set start as the last value of stop, start = 2
stop += row
# increment the new stop-value with how long our last row was (row was 2 in first iteration), stop = 4.
current_num = stop
# set current_num same as the new stop value, current_num = 4.
# we can't replace current_num with stop since we count backwards using current_num (), while still keeping track of the stop value.
Before the next iteration of the forloop, the variables are update once again.
Try to read more about for-loops, to get the hang of how to deal with variables (which ones you need to execute the loop, and which ones you need to update for the function within the loop itself).
Hope this helped, have a nice day!
start is a variable that stores the lower number of each row, wich means that at the end of your loop, it will be set as the number where you stopped last row (stop). It is used as the lower end of your print loop iteration.
stop, is the number where your second loop stops, as the function range(1 , 3) is iterating over [1 , 2] for example (1 before the second parameter)
current_num is set the same, as you are printing in descending order, it is set as the lowest number of your next row, and then the loop itself decrements this variable before printing it in your current row.
Basically, these 3 operations set up your variables for the next iteration of your main loop.

Project Euler Project 67 - Python

I am doing the Project Euler #67 in Python. My program, which worked for Project 18, does not work for Project 67.
Code (excludes the opening of the file and the processing of information):
for i in range(len(temp)):
list1 = temp[i]
try:
list2 = temp[i+1]
trynum1 = list1[lastinput] + max(list2[lastinput],list2[lastinput+1])
try:
trynum2 = list1[lastinput+1] + max(list2[lastinput+1],list2[lastinput+2])
if trynum1 > trynum2:
outputlist.append(list1[lastinput])
else:
outputlist.append(list1[lastinput+1])
lastinput += 1
except IndexError:
outputlist.append(list1[0])
except IndexError:
if list1[lastinput] > list1[lastinput+1]:
outputlist.append(list1[lastinput])
else:
outputlist.append(list1[lastinput+1])
Variables:
temp is the triangle of integers
outputlist is a list which stores the numbers chosen by the program
I know the answer is 7273, but my program finds 6542. I cannot find an error which causes the situation. Please may you help me on it.
Logic
My approach to this program is to find one number (list1[lastinput]) and add it up with the larger number of the two below it (trynum1), compare with the number to the right of the first number (list1[lastinput+1]), adding the larger number of two below it (trynum2). I append the larger one to the output list.
This approach is logically flawed. When you're in row 1, you don't have enough information to know whether moving right or left will lead you to the largest sum, not with only a 2-row lookahead. You would need to look all the way to the bottom to ensure getting the best path.
As others have suggested, start at the bottom and work up. Remember, you don't need the entire path, just the sum. At each node, add the amount of the better of the two available paths (that's the score you get in taking that node to the bottom). When you get back to the top, temp[0][0], that number should be your final answer.
I thought day and night about problem 18 and I solved it, the same way I solved this one.
P.S. 100_triangle.txt is without 1st string '59'.
# Maximum path sum II
import time
def e67():
start = time.time()
f=open("100_triangle.txt")
summ=[59]
for s in f:
slst=s.split()
lst=[int(item) for item in slst]
for i in range(len(lst)):
if i==0:
lst[i]+=summ[i]
elif i==len(lst)-1:
lst[i]+=summ[i-1]
elif (lst[i]+summ[i-1])>(lst[i]+summ[i]):
lst[i]+=summ[i-1]
else:
lst[i]+=summ[i]
summ=lst
end = time.time() - start
print("Runtime =", end)
f.close()
return max(summ)
print(e67()) #7273
Though starting from the bottom is more efficient, I wanted to see if I could implement Dijkstra's algorithm on this one; it works well and only takes a few seconds (didn't time it precisely):
from math import inf
f = open("p067_triangle.txt", "r")
tpyramid = f.read().splitlines()
f.close()
n = len(tpyramid)
pyramid = [[100 - int(tpyramid[i].split()[j]) for j in range(i+1)] for i in range(n)]
paths = [[inf for j in range(i+1)] for i in range(n)]
paths[0][0] = pyramid[0][0]
def mini_index(pyr):
m = inf
for i in range(n):
mr = min([i for i in pyr[i] if i >= 0]+[inf])
if mr < m:
m, a, b = mr, i, pyr[i].index(mr)
return m, a, b
counter = 0
omega = inf
while counter < n*(n+1)/2:
min_weight, i, j = mini_index(paths)
if i != n-1:
paths[i+1][j] = min( paths[i+1][j], min_weight + pyramid[i+1][j])
paths[i+1][j+1] = min( paths[i+1][j+1], min_weight + pyramid[i+1][j+1])
else:
omega = min(omega, min_weight)
paths[i][j] = -1
counter += 1
print(100*n - omega)
Here is my solution. Indeed you have to take the bottom - up approach.
Result confirmed with PE. Thanks!
def get_triangle(listLink):
triangle = [[int(number) for number in row.split()] for row in open(listLink)]
return triangle
listOfLists = get_triangle('D:\\Development\\triangle.txt')
for i in range(len(listOfLists) - 2, -1, -1):
for j in range(len(listOfLists[i])):
listOfLists[i][j] += max(listOfLists[i+1][j], listOfLists[i+1][j+1])
print(listOfLists[0][0])

Python 3: Optimised Bubble Sort

please help. I need to optimize my Bubble Sort algorithm in order to get less total comparisons than the non-optimised bubbleSort. I managed to create just the 'Normal Bubble sort (only travels from left to right):
def bubbleSort(values):
n = len(values) - 1
swap = True
ncomp = 0 # My total comparisons counter
while swap:
swap = False
for i in range(n): # i = 0, 1, 2, ..., n-1
ncomp += 1
if values[i] > values[i+1]:
temp = values[i]
values[i] = values[i+1]
values[i+1] = temp
swap = True
return values, ncomp
So basically i dont know how to create an 'optimised bubbleSort', a bubbleSortPlus function in which bubbles travel in both directions: from left to right, immediately followed by a travel from right to left. In theory, in each pass the travel of the bubbles should be shortened (saving in a variable the position of the last swap in a travel, and make the next travel start at that position. I tried so hard but i'm just a python newbie, please help.
Here's some skeleton code that shows how to scan the array forwards and backwards, while shrinking the list on each iteration.
values = 100,101,102,103,104,105
start = 0
stop = len(values)-1
while stop > start:
for i in range(start, stop):
print i, "compare", values[i], "with", values[i+1]
print "moved a large value to index", stop
print
stop = stop - 1
if stop == start:
break
for i in range(stop, start, -1):
print i, "compare", values[i], "with", values[i-1]
print "moved a small value to index", start
print
start = start + 1
I guess it has been optimized...
The naive Bubble sort does not include the swap flag. So it will not return until finish all O(n^2) comparisons in any cases. But with swap flag, the number of comparison will be almost linear if the input sequence has been "almost sorted".

two random inequalities as conditionals for an if-then loop in python

I am in a conundrum. I am trying to figure out why the incrementing counter isn't increment. It returns a value of only one. The idea is that are a random time that incrementing counter t_years would go down to zero and begin again while another counter would just keep running.
import time, math, random
t0 = time.time()
average_life_span = .10
while True:
time.sleep(.01)
a = time.time()
age = int (a*1000) - int(t0*1000) #millis
t_years = 0
while (int (age) < int (random.normalvariate (average_life_span, 1))):
t_years = 0
else:
t_years = t_years + .01
print age, t_years
You set t_years = 0 before the inner loop, then again each time through the loop.
When you finally finish the inner loop, in the else clause, you add .01. So now it's guaranteed to be 0.01.
The next time through the outer loop, you again reset it to 0, and then do so over and over again, and finally add .01 to that at the end. So it's again guaranteed to be 0.01.
If you want the number to start at 0 and increment by 0.01 each time through the outer loop, don't keep resetting it to 0. Do this:
t_years = 0
while True:
time.sleep(.01)
a = time.time()
age = int (a*1000) - int(t0*1000) #millis
while (int (age) < int (random.normalvariate (average_life_span, 1))):
pass
else:
t_years = t_years + .01
print age, t_years
I'm not sure what this loop is actually supposed to be doing. I don't think it will ever be triggered, but even if it is, all it can do is delay for a random amount of time burning as much CPU as possible while doing so. Why would you want to do that?

Categories

Resources