Counting down and then up - python

I'm new to python and trying to run a function that will, given one variable, count down to zero and then up to the original variable. the output should look something like this:
>>> functionname(5)
5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5
so far I've written the code below, but this doesn't count up all the way to the original variable. I guess I need to somehow save the variable in order to refer to it later, but I have no idea how to do that, since python automatically changes n as the function goes on.
def functionname(n):
n = orginal
while n > 0:
print n
n=n-1
print n
if n==0:
print n
n=n+1
I would be very grateful for some pointers, as I seem to be completely stuck at the moment.

Just count from negative to positive and use math:
def fn(n):
print ', '.join(str(abs(i)) for i in range(-n, n+1))
fn(5)
# 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5
Pointers:
If you already know the range you want to iterate over, it's much cleaner and more direct to use a for loop instead of a while.
You can use the range function to generate number sequences.
You can convert simple for loops into list-comprehensions as I did above.
The "simple" clean implementation of your requirements would look something like this:
def fn(n):
for i in range(n, 0, -1):
print i,
for i in range(n + 1):
print i,
Other notes:
range can count backwards too
The end argument to range isn't included in the range itself, that's why the second loop specifies n + 1 as the limit (instead of just n)
Adding a trailing comma on your print makes it add a space at the end instead of a line-break.

Your second block is an if n == 0: (which you know it is since the while loop terminated when n hit 0); presumably you want while n <= 5.
Note that there are nicer ways to accomplish the same thing in Python. For example, using a pair of ranges with itertools.chain to iterate each range one after another allows you to simplify to:
import itertools
def functionname(n):
for i in itertools.chain(range(n, 0, -1), range(n+1)):
print i

Personally, I'd do something like...
def count(n):
for x in range(n, -n, -1):
print(str(abs(x)) + ",")
At the suggestion of dlewin, here's a list comprehension of the same...
def count(n):
print(','.join(str(abs(x)) for x in range(n, -n, -1)))

You need a second while loop that starts at 0 and goes back up to "original".
Do you know about "for" loops yet? Those are better for counting.

Your idea about having original is correct however you are using the assignment operator the wrong way 'round. Also the if n==0 line should be another loop (while or for as suggested by other answers), counting back up to original.
So I'd start with copying the value from n to original like this:
original = n
Hope that helps!

You got some bad formatting there. Remember to indent properly for functions and while and if statements.
So first, set n to 5. Then count down from there until you reach 0 with a while loop:
while n != -1:
print n
n -= 1
Then after the loop breaks, count back up again and reset n to 0:
n = 0
while n < 6:
print n
n += 1

Related

How to generate infinite sequence with itertools

I would like to generate two infinitive series of 0 and 1 and specifically in the following order:
0, 1, 0, -1, 0, 1, 0, -1, ...
I have created the following code which does not return what except:
# for in loop
for i in itertools.cycle(range(0,2)):
if i == 0:
i += 1
if i == 1:
i -= 1
if i == 0:
i -= 1
print(i, end = " ")
It just returns a series of -1. Cannot figure out where the error is. Can anyone give any suggestions
You can use itertools.cycle() in a way that explicitly states the elements you want to generate:
from itertools import cycle
cycle([0, 1, 0, -1])
As an alternative, you can implement your own generator with some simple modular arithmetic:
def seq():
i = 0
while True:
yield (-1)**(i // 2) * (i % 2)
i = (i + 1) % 4 # keeps i small, so as not to take up too much memory
Demo:
>>> s = seq()
>>> [next(s) for _ in range(10)]
[0, 1, 0, -1, 0, 1, 0, -1, 0, 1]
I know OP states that they specifically want to use itertools, but this may still be helpful to others.
As pointed out in a comment, you can also simply emulate itertools.cycle by passing arbitrary args and using yield from (note that itertools.cycle actually works differently under the hood):
def seq(*args):
while True:
yield from args
s = seq(0, 1, 0, -1)
But that's not as fun as figuring out the actual arithmetic sequence in my opinion :D
Not sure why you're insisting on using itertools.cycle in a for loop like this, but here's one way you could make this work:
for i in itertools.cycle(range(0, 2)):
if i == 0:
print(i, end=" ")
i += 1
print(i, end=" ")
i -= 1
print(i, end=" ")
i -= 1
print(i, end=" ")
Note that there's really no point in using your nested if statements, since each nested predicate will always be true: if i is zero, and then you add one to it, then of course your nested predicate if i == 1 will be true...
In addition, your use of itertools.cycle in a for loop is an anti-pattern. As shown in BrokenBenchmark's answer, you can simply cycle over the elements of the sequence itself, forever. If you insist on using the for loop:
for i in itertools.cycle([0, 1, 0, -1]):
print(i, end=" ")
There are so many things wrong with this approach though. Namely, as I discussed in the comments, this is an infinite loop which is a fundamentally different behavior than an infinite sequence. With an infinite loop, nothing else can ever happen -- your program will be stuck cycling over your four elements forever with no way to do anything else.
If you use itertools.cycle as it's meant to be used -- as a generator -- you can pick up where you left off in the sequence at any time, allowing you to perform other tasks for as long as you want before continuing your sequence:
c = itertools.cycle((0, 1, 0, -1))
next(c) # 0
next(c) # 1
next(c) # 0
next(c) # -1
# do something else for a while
next(c) # 0
# yield the next 10 items from the sequence
for _ in range(10):
print(next(c), end=" ")
# do something else again

Building a function to return integer until reaching 1 in Python

I am trying to build a function that will take a number and return a list. If the number is even, the second integer of the list will be the number divided by two. If it is odd, then following number will be number multiplied by 3 plus one and so on until it reaches 1.
For example:
Function(5) will give
[5, 16, 8, 4, 2, 1]
My code is:
def Collatz(n):
out=[]
while n>1:
if n%2==0:
out.append(n/2)
elif n%2!=0:
out.append((n*3)+1)
print(out)
Collatz(20)
It doesn't produce anything. Any suggestion? I want to do this with while loop.
This code is close to be working, a few insights and improvements:
Change n inside the while loop (otherwise n remains larger than 1 and you get infinity loop)
Use lower case in function names and use underscore to separate between words
def collatz(n):
out = [n]
while n > 1:
if n % 2 == 0:
n = int(n / 2)
elif n % 2 != 0:
n = (n * 3) + 1
out.append(n)
print(out)
# [5, 16, 8, 4, 2, 1]
collatz(5)
It is because the while loop never terminates, this is because you aren't actually changing the value of n each time. So before you append to the array say something like n=n/2 (or whatever the calculation is) then append n. This means that n will eventually reach one and you get a print out
The problem with your code is you are not decrementing the value of n which is leading to an infinite loop.
Every time when you are appending a new value in the list you also need to update the value for n.
Try this:
def Collatz(n):
out=[]
while n>1:
if n%2==0:
out.append(n//2)
#updating n
n= n//2
elif n%2!=0:
out.append((n*3)+1)
#updating n
n= (n*3)+1
print(out)
Collatz(20)
Output:
[10, 5, 16, 8, 4, 2, 1]
Try this:
def Collatz(n):
l=[n]
while l[-1]!=1:
x=l[-1]/2 if l[-1]%2==0 else l[-1]*3+1
l.append(int(x))
return l
Here's a version that works.
def Collatz(n):
out=[n]
while n>1:
if n%2==0:
n = n/2
else:
n = (n*3)+1
out.append(int(n))
return out
print(Collatz(5))
Some notes on your version:
Your loop never updates "n" so n stays at its initial value forever, leaving you in an infinite loop.
The "elif" can be simply "else" - if the value is not even, then it's odd (assuming we're working with integers)
If you want the initial value to be in the list, we should probably initialize the list that way.
In your problem statement, you say you need the function to "return a list". Printing from inside the function is fine if that's really all you need, but if we actually return the list with a return statement, then the list it generates can be used by other code.
If you're using Python 3, the operation n/2 will return a float, even if n was an integer to start. If we only want integers in our list, we need to do one of two things:
Make it explicitly integer division with n//2
Or re-cast n to an integer with int(n) before we insert it into the list. (Which is how I did it here.)
Notice that your code doesn't change n.Therefore your loop never terminates. Check this out:
def collatz(n):
my_list = [n]
while n > 1:
if n % 2:
n = n * 3 + 1
else:
n = n / 2
my_list.append(n)
return(my_list)

Finding a pair of values which add up to given sum

lst = [3, 4, 1, 2, 9]
givenSum = 12
table = {}
x = 0
y = 0
for i in range(0, len(lst)):
table[givenSum - lst[i]] = 1
i += 1
for x in table:
for y in table:
if (x + y) == givenSum:
print(x, "and", y, "is equal to", givenSum)
break
This is the output
9 and 3 is equal to 12
3 and 9 is equal to 12
I don't know why it's being shown up twice. I need to find a pair of values that add up to the given sum and this is the only way I could think of. I only want it to show up once though any ideas on how I can do that?
There are better solutions, but to fix your issue making minimal changes to your code:
lst = [3, 4, 1, 2, 9]
givenSum = 12
for x in range(0, len(lst) - 1):
for y in range(x + 1, len(lst)):
if lst[x] + lst[y] == givenSum:
print(lst[x], "and", lst[y], "is equal to", givenSum)
break
This will print
3 and 9 is equal to 12
Note that the redundant table is completely removed from the code.
If you run it for a better test case:
lst = [3, 4, 5, 6, 7, 1, 2, 9]
it will print
3 and 9 is equal to 12
5 and 7 is equal to 12
First, to address why the looping continues and gives a second output, break can only break out of its immediate loop. Since you have a nested loop, the break only stops the for y in table: inner loop, but allows for x in table outer loop to move onto it's next iteration. So eventually, x is able to take the value of 3 later on, thus giving you the two outputs you see.
So, if you need a way to stop the iteration entirely when a solution is found, you need to either chain the break statements using a for else syntax (which arguably might be tough to read) as follows,
for x in table:
for y in table:
if (x + y) == givenSum:
print(x, "and", y, "is equal to", givenSum)
break #breaks from inner loop
else: #for else syntax: this block runs if and only if there was no break encountered during looping.
continue #jumps the outer loop to next iteration
break #this break is set at outer loop's level. Essentially, we can only reach this portion if there is a break in the inner loop.
For else says: run through the whole iteration, and if no break is found, executes the code in the else block. Essentially, the "else" of a "for else" is like a "for - no break".
However, one easier alternative is to use a function with a return (which also makes it easier to read the code).
def find_given_sum(lst, givenSum):
table = {}
x = 0
y = 0
for i in range(0, len(lst)):
table[givenSum - lst[i]] = 1
i += 1
for x in table:
for y in table:
if (x + y) == givenSum:
print(x, "and", y, "is equal to", givenSum)
return #this returns immediately out of the function, thus stopping the iteration.
Also, you could just repeat the break condition, but repeating code is generally not a good practice.
Hope this helps address why the two outputs are being printed. Now, as for the solution itself, there's actually a much better way to solve this. It builds upon the idea of compliments, which you seem to have a sense of in your table. But it doesn't require iteration over the table itself. As a hint: the ideal solution runs in O(n) time. I will not discuss the ideal solution, but hope this prompts you to find the best approach.
Looping twice for n elements costs you O(N^2) time, which is inefficient for large lists. Modified and tested the code to use hash map/dictionary to store the list elements and now it will take only O(N) time.
Map = dict()
lst = [3, 4, 1, 2, 9]
givenSum = 12
for i in range(0, len(lst)):
rem=givenSum-lst[i]
if rem in Map:
print lst[i],lst[Map[rem]]
else:
Map[lst[i]]=i
Store the value of each list element into the map whenever it does not exist in the map.
At each iteration, take the difference between givenSum and current element at that iteration and then search for that difference in the Map.
If it exists it means the pair exists or else not.
In this approach you are running the loop only once, which takes O(N) time because accessing elements in hash map is O(1)/constant time.
Use itertools to get the result
import itertools
sum = 10
lst = [3, 4, 1, 2, 9]
ans = list(filter(lambda x : x[0]+x[1] == sum,itertools.combinations(lst, 2)))

replace while loop with range function

Can I replace while loop with range function. I am using following code
check = 0
while check<5:
print check
check+=2
I am writing in following way
for _check in range(0,5,2):
print _check
is it correct way?
> Editing My question
if I am not using _check variable inside for loop. Can I avoid to declare also
Yes, you are using range() correctly, but you may want to use xrange() instead here:
for check in xrange(0, 5, 2):
print check
xrange() produces the same results when iterated over, but doesn't build a whole list of all possible indices first, and as a result uses less memory:
>>> range(0, 5, 2)
[0, 2, 4]
>>> xrange(0, 5, 2)
xrange(0, 6, 2)
Since the end-point is not included in the values, it doesn't matter if you use 5 or 6 here as the endpoint, it is just calculated for you from the input parameters.
If you are not using the loop variable, you can use _ to indicate that you are ignoring it in the loop. This is just a naming convention:
for _ in xrange(0, 5, 2):
# do something 3 times.
in which case you may as well just calculate how many indices there are between 0 and 5 with a step of two and simplify your loop to:
upper_limit, step = 5, 2
for _ in xrange((upper_limit - 1 + step) // step):
# do something 3 times.
Regarding the original while loop, using check = check + 1
for x in xrange(5):
print x
See the documentation here for more information on control flow tools (like loops).
EDIT
If you want to increment by 2 during each iteration, you can use the above code with the increment specified:
for x in xrange(0,5,2):
print x

While in a for loop

I want to know the number of zeros which first appear in a list, before any other number.
For example:
L1 = [0, 0, 0, 0, 0, 1, 2] - the output should be 5, which is the number of zeros.
L2 = [1, 0, 0, 0, 0, 0, 2] - the output should be zero. Although there are 5 zeros in this list but the list starts with 1.
Here is my code:
k = 0
for i in L1:
while i == 0:
k = k + 1
It doesn't work though. I think it is an infinite loop, but I don't know why.
Think of what will happen the first time i gets set to 0.
The while loop will start and never stop, because i is not changed within that loop.
You would be better off with something as per the following transcript, a slight modification of yours:
>>> list1 = [0,0,0,0,0,1,2]
>>> count = 0
>>> for item in list1:
... if item == 0:
... count = count + 1
... else:
... break
...
>>> print count
5
or the slightly shorter variation which breaks immediately for a non-zero value, adding one otherwise.:
>>> list1 = [0,0,0,0,0,1,2]
>>> count = 0
>>> for item in list1:
... if item != 0: break
... count = count + 1
...
>>> print count
5
As other commentators have said, the problem in your code is that you seem to misunderstand the meaning of the while keyword. That aside, for problems like these, I often prefer a more functional style:
>>> import itertools
>>> k = len(list(itertools.takewhile(lambda x: x == 0, L1)))
>>> k
5
>>> k = len(list(itertools.takewhile(lambda x: x == 0, L2)))
>>> k
0
If you are just beginning to get to know Python, playing around with what the itertools offers is well worth it.
My answer expands on paxdiablo's. Thanks to him for clarifying OP's intent with the L2 case, which I had misread.
While itertools is handy for this kind of thing, at the point you're at, I'd say mastering the basic language features is worthwhile.
In your code, you do this:
L1 = [0, 0, 0, 0, 0, 1, 2]
k = 0
for i in L1:
while i == 0:
k = k + 1
When you run that chunk of code on L1, it loops forever. Here's why:
for causes the code that's enclosed (meaning the code below that's set aside by indentation) to loop. So, your for i in L1: loop runs everything indented below it once for each thing in L1.
But, while does something similar. while tells Python to run the indented code below it over and over until the condition on the while statement is False.
So, in the case of L1, the first time through the for loop, i gets set to 0. Then, while i == 0: says to execute the code enclosed, k = k + 1, over and over again until i is no longer zero. Unfortunately, since the code in the while loop does not change the value of i, i will be zero until the end of time, so it runs forever, repeatedly adding one to k.
What you're looking for is an if statement, which will not loop. It will just run the enclosed code, or not, based on whether its test is true. So, instead of:
while i == 0:
k = k + 1
you can use
if i == 0:
k = k + 1
else:
break
Then, each time through the for loop, the code asks whether i is zero, and if so, only once, it adds one to k, then goes on to the next list element. To cover your L2 case, if you hit a number that is not zero, break exits the for loop and stops counting.
The answer presented with itertools is clever, useful, good to know, and probably what you'd want to use for very large lists, but since it appears you're just learning the language (and probably learning your first language), it's worth learning how to use for, while, and if correctly.
What you're thinking while does, is carry on as normal until the condition isn't met. What it really does, is repeat the code inside of the while as a loop until the condition isn't met.
Here's my solution, which tries to work as similarly to yours as possible, by carrying on counting until the number isn't a 0 then looking at how far it's come and breaking out of the for loop.
k = 0
for index,item in enumerate(L1):
if item != 0:
k = len(L1[:index])
break

Categories