Python list with zeros and numbers repeating - python

My intent is to generate a generic empty list and then to append it with a numeric sequence such that it gives zeros but at the third place it gives 3 and then its multiples, that is a[(0,0,3,0,0,6,0,0,9)] and it needs to have 100 values inside.
I first set the list and then I use the 'for' loop in a range(0,100) and I am sure I need to use % in ways such that whenever my sequence going 1 to 100 is perfectly divisible by 3 it gives back 3 (and not 0) but then it keeps evolving in 6,9,12.. How do I do?
for i in range(0,100):
if i%3==0:
return 0
else
return 3
Of course this is completely wrong but i am new to programming in general. Thanks in advance.

you could try this:
for i in range(0, 100, 3):
list[i]=i
You just change the "step" of the range function, so the index i will represent also the value passed in the list, it should work properly.

#Mark's comment is very relevant and makes good use of the modulo and list comprehension properties in a very simple way. Moreover, its code easily adapts to any value.
However the modulo operator is quite slow so here are other ways to achieve the same result.
Method 1
We can make a range from 3 to 101 with a step of 3, and add [0, 0, i] to each step. Since there will be missing zeros at the end of the list, we must add as many as the rest of the division of 100 by 3.
data = [num for i in range(3, 101, 3) for num in [0, 0, i]] + [0] * 1
Method 2
With the same idea, we can use .extend() to add two 0s before each term.
data = []
for i in range(3, 101, 3):
data.extend([0, 0, i])
data.append(0)
Method 3
The simplest idea, we create a list of 100 zeros, and then we modify the value every 3 terms.
data = [0] * 100
for i in range(2, 101, 3):
data[i] = i + 1
Comparison
Using timeit, here is a comparison of the speed of each algorithm, the comparison is based on 10000 repetitions.
import timeit
print(timeit.timeit("data = [0 if n % 3 else n for n in range(1, 101)]"))
print(timeit.timeit("data = [num for i in range(3, 101, 3) for num in [0, 0, i]] + [0] * 1"))
print(timeit.timeit("""
data = []
for i in range(3, 101, 3):
data.extend([0, 0, i])
data.append(0)
"""))
print(timeit.timeit("""
data = [0] * 100
for i in range(2, 101, 3):
data[i] = i + 1
"""))
Output:
4.137781305000317
3.8176420609997876
2.4403464719998738
1.4861199529996156
The last algorithm is thus the fastest, almost 3 times faster than using modulus.

A function stops running, when it encounters a return. So in your code, you only ever execute the loop once.
But, what if we could change that? Do you know what a generator is? Have a look at this:
def multiples_of_three_or_zero():
for i in range(0,100):
if i%3==0:
yield 0
else
yield 3
That's a generator. yield doesn't end the execution, but rather suspends it. You use it like this:
for i in multiples_of_three_or_zero():
print(i)
Or, if you really want all the elements in a list, just make a list from it:
list(multiples_of_three_or_zero())

Ok I practiced a little bit and I found this solution which best suits my needs (it was for a take-home exercise)
A=[]
for a in range (1,101):
if a%3==0:
A.append(a)
else:
A.append(0)
print(A)
thanks you all!

Related

in Python, range(1, 100, *10) doesn't work?

I have a problem with range() or the for loop.
in C++, the for loop can be used like this:
for(int i=1;i<=100;i*=10){
//lala
}
My Python script that causes the error looks like this:
li = [x for x in range(100, 0, /10)]
print(li)
I hope to get li = [100, 10, 1]
How can I achieve this without the use of li.append ( 100, 10, 1 )?
You can't use range for this. The third argument for range is a fixed step used to increment the value: it can't be used to multiply or divide the previous value.
Instead, use a while loop:
i = 1
while i < 100:
...
i *= 10 # or i /= 10 depending on what you want.
If you really want to use a list comprehension, you can do as suggested by Albin Paul:
li = [10**x for x in range(3) ] # [1, 10, 100]
li = [10**x for x in range(2, -1, -1) # [100, 10, 1]
You can simply use a generator expression, like
for i in (10 ** x for x in range(2, -1, -1)):
print(i)
# 100
# 10
# 1
Or, if you want your "power range" to be a reusable function, you can create your own, i.e.
def powrange(*args, base=10):
for i in range(*args):
yield base ** i
for i in powrange(2, -1, -1, base=10):
print(i)
# 100
# 10
# 1
The built-in range is not meant to be used this way. It takes three arguments: range(start, stop, step) and goes from start to stop with a constant stepsize.
If you want to use a single function, you could use numpy's logspace
It looks like this:
import numpy
li = numpy.logspace(0, 2, base=10, dtype = int)
# li is [1, 10, 100]
# goes from base^0 to base^2, so from 1 to 100
If you don't want to rely on numpy, the solution with a while loop seems very pythonic to me.
Benchmark time :D
A comment pointed out that this solution might be:
non obvious
slow
The first point is a matter of taste, but I disagree on performance:
reps = 100000
list_elems = 100
start = time.perf_counter()
for i in range(reps):
np.logspace(0, list_elems, base=2, dtype=int)
end = time.perf_counter()
numpy_time = end - start
start = time.perf_counter()
for i in range(reps):
[2**x for x in range(list_elems)]
end = time.perf_counter()
python_time = end - start
# reps = 100000
# list_elems = 100
# numpy took 1.70, python took 2.86
# reps = 100000
# list_elems = 10
# numpy took 1.65, python took 0.242
Of course, this depends a lot on the number of list elements that you want to produce. Numpy introduces overhead but scales better. So for shorter lists, python wins.
To change make a log(n) time complexity loop you need the log function or use while loops to do the job
In [10]: import math
In [9]: [10**x for x in range( 0,int(math.log10(1000)))]
Out[9]: [1, 10, 100]
I still think while loops are the way to go
The arguments for range are: start, stop and step, where the step is int.
Maybe you want to see the result as this:
li = [10**x for x in range(0, 3, 1)]
print(li)
[1, 10, 100]
Overall take a look at a nice learning course, for example, "Automate the Boring Stuff with Python":
Here is the video right about loops
And here is the whole
course (available online for free)
When you use the for loop as
with x in range(start,stop,step)
The way it takes place is that first
Edit: The first part is explanation why it won't work
You get a list generated by the range function(Not since python3 though), now it only returns a function range and its current state, this can be of float or int, so using /10 as step there will do nothing other than Invalid syntax, because range only accepts integers.
Only after this does x gets initialized and starts taking values present in the dictionary in an indexed way.
You can however use
for y in range(2,-1,-1):
x = (10**y)
Welcome to python and happy coding.
Thanks to #GPhilo for pointing out the errors.

Iterate a huge list of integers which grow exponentially, where the exponent is 1.5

I need to iterate a list of integers:
2, 3, 4, 6, 9, 13, 19, 28, 42, ...
So the general rule is list[i+1] = list[i]*3/2.
The list should end at or right before 10^34.
This means that the last element is a number between 10^34*2/3 and 10^34.
I obviously cannot have this list preallocated in memory, so something like:
list = [2]
while True:
next = list[-1]*3/2
if next > 10**34:
break
list.append(next)
Is out of the question.
I can of course simply use the above in order to iterate these integers without having them stored in a list or generated by an iterator of some sort.
But the problem is that I have nested loops, like so:
for i in xrange(...):
for j in xrange(...):
for m in xrange(...):
for n in xrange(...):
So breaking this into several while loops would make the code pretty horrible.
Ideally, I would like to have some sort of xrange which generates this list of numbers "on the fly" (as xranges normally do).
As I mentioned in my comment, the list won't actually be very long. You could therefore initialise it as:
[int(2 * (1.5 ** n)) for n in range(int(math.log(10 ** 34, 1.5) - 1))]
However, this is actually slightly different to the example you gave, wherein you round to integer before generating the next number. In this case, you would have to do something iterative instead (as far as I can tell):
i = 2
lst = []
while i < 10 ** 34:
lst.append(i)
i = int(i * 1.5)
This might be what you're looking for:
lst = [2*1.5**i for i in range(0,192)]
The last term would be 2*1.5^191.
If you want them all to be integers you could say use the int() cast.
But you should note that the time/memory to do this will probably be similar to what you were doing in your example code. They are both doing similar things in the end.
If you want them all to be integers throughout the process:
i = 2
lst = [2]
while i < 10**34:
lst.append(int(i*=1.5))
Again, this will only save a trace amount of memory/time.

Two value in two lists, simplify the code

I have two lists, and I want to compare the value in each list to see if the difference is in a certain range, and return the number of same value in each list. Here is my code 1st version:
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k=0
for i in xrange(0, len(m)):
for j in xrange(0, len(n)):
if abs(m[i] - n[j]) <=0.5:
k+=1
else:
continue
the output is 3. I also tried 2nd version:
for i, j in zip(m,n):
if abs(i - j) <=0.5:
t+=1
else:
continue
the output is 1, the answer is wrong. So I am wondering if there is simpler and more efficient code for the 1st version, I have a big mount of data to deal with. Thank you!
The first thing you could do is remove the else: continue, since that doesn't add anything. Also, you can directly use for a in m to avoid iterating over a range and indexing.
If you wanted to write it more succiently, you could use itertools.
import itertools
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k = sum(abs(a - b) <= 0.5 for a, b in itertools.product(m, n))
The runtime of this (and your solution) is O(m * n), where m and n are the lengths of the lists.
If you need a more efficient algorithm, you can use a sorted data structure like a binary tree or a sorted list to achieve better lookup.
import bisect
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
n.sort()
k = 0
for a in m:
i = bisect.bisect_left(n, a - 0.5)
j = bisect.bisect_right(n, a + 0.5)
k += j - i
The runtime is O((m + n) * log n). That's n * log n for sorting and m * log n for lookups. So you'd want to make n the shorter list.
More pythonic version of your first version:
ms = [1, 3, 5, 7]
ns = [1, 4, 7, 9, 5, 6, 34, 52]
k = 0
for m in ms:
for n in ns:
if abs(m - n) <= 0.5:
k += 1
I don't think it will run faster, but it's simpler (more readable).
It's simpler, and probably slightly faster, to simply iterate over the lists directly rather than to iterate over range objects to get index values. You already do this in your second version, but you're not constructing all possible pairs with that zip() call. Here's a modification of your first version:
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k=0
for x in m:
for y in n:
if abs(x - y) <=0.5:
k+=1
You don't need the else: continue part, which does nothing at the end of a loop, so I left it out.
If you want to explore generator expressions to do this, you can use:
k = sum(sum( abs(x-y) <= 0.5 for y in n) for x in m)
That should run reasonably fast using just the core language and no imports.
Your two code snippets are doing two different things. The first one is comparing each element of n with each element of m, but the second one is only doing a pairwise comparison of corresponding elements of m and n, stopping when the shorter list runs out of elements. We can see exactly which elements are being compared in the second case by printing the zip:
>>> m = [1,3,5,7]
>>> n = [1,4,7,9,5,6,34,52]
>>> zip(m,n)
[(1, 1), (3, 4), (5, 7), (7, 9)]
pawelswiecki has posted a more Pythonic version of your first snippet. Generally, it's better to directly iterate over containers rather than using an indexed loop unless you actually need the index. And even then, it's more Pythonic to use enumerate() to generate the index than to use xrange(len(m)). Eg
>>> for i, v in enumerate(m):
... print i, v
...
0 1
1 3
2 5
3 7
A rule of thumb is that if you find yourself writing for i in xrange(len(m)), there's probably a better way to do it. :)
William Gaul has made a good suggestion: if your lists are sorted you can break out of the inner loop once the absolute difference gets bigger than your threshold of 0.5. However, Paul Draper's answer using bisect is my favourite. :)

How to count by twos with Python's 'range'

So imagine I want to go over a loop from 0 to 100, but skipping the odd numbers (so going "two by two").
for x in range(0,100):
if x%2 == 0:
print x
This fixes it. But imagine I want to do so jumping two numbers? And what about three? Isn't there a way?
Use the step argument (the last, optional):
for x in range(0, 100, 2):
print(x)
Note that if you actually want to keep the odd numbers, it becomes:
for x in range(1, 100, 2):
print(x)
Range is a very powerful feature.
(Applicable to Python <= 2.7.x only)
In some cases, if you don't want to allocate the memory to a list then you can simply use the xrange() function instead of the range() function. It will also produce the same results, but its implementation is a bit faster.
for x in xrange(0,100,2):
print x, #For printing in a line
>>> 0, 2, 4, ...., 98
Python 3 actually made range behave like xrange, which doesn't exist anymore.
for i in range(0, 100, 2):
print i
If you are using an IDE, it tells you syntax:
min, max, step(optional)

Number of multiples less than the max number

For the following problem on SingPath:
Given an input of a list of numbers and a high number,
return the number of multiples of each of
those numbers that are less than the maximum number.
For this case the list will contain a maximum of 3 numbers
that are all relatively prime to each
other.
Here is my code:
def countMultiples(l, max_num):
counting_list = []
for i in l:
for j in range(1, max_num):
if (i * j < max_num) and (i * j) not in counting_list:
counting_list.append(i * j)
return len(counting_list)
Although my algorithm works okay, it gets stuck when the maximum number is way too big
>>> countMultiples([3],30)
9 #WORKS GOOD
>>> countMultiples([3,5],100)
46 #WORKS GOOD
>>> countMultiples([13,25],100250)
Line 5: TimeLimitError: Program exceeded run time limit.
How to optimize this code?
3 and 5 have some same multiples, like 15.
You should remove those multiples, and you will get the right answer
Also you should check the inclusion exclusion principle https://en.wikipedia.org/wiki/Inclusion-exclusion_principle#Counting_integers
EDIT:
The problem can be solved in constant time. As previously linked, the solution is in the inclusion - exclusion principle.
Let say you want to get the number of multiples of 3 less than 100, you can do this by dividing floor(100/3), the same applies for 5, floor(100/5).
Now to get the multiplies of 3 and 5 that are less than 100, you would have to add them, and subtract the ones that are multiples of both. In this case, subtracting multiplies of 15.
So the answer for multiples of 3 and 5, that are less than 100 is floor(100/3) + floor(100/5) - floor(100/15).
If you have more than 2 numbers, it gets a bit more complicated, but the same approach applies, for more check https://en.wikipedia.org/wiki/Inclusion-exclusion_principle#Counting_integers
EDIT2:
Also the loop variant can be speed up.
Your current algorithm appends multiple in a list, which is very slow.
You should switch the inner and outer for loop. By doing that you would check if any of the divisors divide the number, and you get the the divisor.
So just adding a boolean variable which tells you if any of your divisors divide the number, and counting the times the variable is true.
So it would like this:
def countMultiples(l, max_num):
nums = 0
for j in range(1, max_num):
isMultiple = False
for i in l:
if (j % i == 0):
isMultiple = True
if (isMultiple == True):
nums += 1
return nums
print countMultiples([13,25],100250)
If the length of the list is all you need, you'd be better off with a tally instead of creating another list.
def countMultiples(l, max_num):
count = 0
counting_list = []
for i in l:
for j in range(1, max_num):
if (i * j < max_num) and (i * j) not in counting_list:
count += 1
return count

Categories