This is my code for finding primes using the Sieve of Eratosthenes.
list = [i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]
for i in list:
for a in list:
if a!=i and a%i == 0:
list.remove(a)
Trying to find a way to compress those nested for loops into some kind of generator or comprehension, but it doesn't seem that you can apply a function to a list using a comprehension. I tried using map and filter, but I can't seem to get it right.
Thinking about something like this:
print map(list.remove(a), filter(lambda a, i: (a%i ==0 and a!=i), [(a, i) for i in list for a in list])
Obviously doesn't work for a dozen reasons. If I just was using the filter portion of that code:
filter(lambda a, i: (a%i ==0 and a!=i), **[(a, i) for i in list for a in list]**
What's the proper way of putting two variables into the lambda? (a,i) makes it a tuple, but I want to submit 'a' and 'i' as independent variables to put into the lambda.
I ended up resolving the problem with this one-liner:
print sorted(set([i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]).difference(a for i in l for a in l if a!=i and a%i == 0))
The first thing to note is that what you have written is not the sieve of eratosthenes. Look how many loops a totally naive sieve of eratosthenes executes:
def sieve1(n):
loops = 0
numbers = set(range(2, n))
for i in range(2, int(n ** 0.5) + 1):
for j in range(i * 2, n, i):
numbers.discard(j)
loops += 1
return sorted(numbers), loops
Tested:
>>> sieve1(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
178)
178 loops for 100 numbers (not including the sort). This can be improved with a few minor changes:
def sieve2(n):
loops = 0
numbers = range(0, n)
for prime in numbers:
if prime < 2:
continue
elif prime > n ** 0.5:
break
for i in range(prime ** 2, n, prime):
numbers[i] = 0
loops += 1
return [x for x in numbers if x > 1], loops
Tested:
>>> sieve2(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
102)
102 loops for 100 numbers (not including the filter at the end). Now look at yours:
def sieve3(n):
loops = 0
numbers = range(2, n)
for i in numbers:
for j in numbers:
if j != i and j % i == 0:
numbers.remove(j)
loops += 1
return numbers, loops
Tested:
>>> sieve3(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
663)
It gets worse:
>>> [sieve1(x)[1] for x in [100, 1000, 10000]]
[178, 2978, 41723]
>>> [sieve2(x)[1] for x in [100, 1000, 10000]]
[102, 1409, 16979]
>>> [sieve3(x)[1] for x in [100, 1000, 10000]]
[663, 28986, 1523699]
At n = 10000, your implementation does almost 100x as much work!
My suggestion would be to create a sensible implementation before making it "compact." Code golf can be fun, but it's nowhere near as challenging or as edifying as writing efficient code, whatever the length.
That said, consider this one-liner (if you don't count the import, which you could get rid of by using lambda x, y: x - y in place of operator.sub). This implements the first algorithm with a small improvement:
>>> from operator import sub
>>> reduce(sub, (set(range(x ** 2, 100, x)) for x in range(2, int(100 ** 0.5) + 1)), set(range(2, 100)))
set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
It's not precisely a direct translation of your loops, but it's quite close and compact:
>>> l = range(2, 101)
>>> sorted(set(l).difference(a for i in l for a in l if a!=i and a%i == 0))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Although I'd suggest a > i rather than a != 0 as being shorter and faster ;)
You are not doing the Sieve of Eratosthenes; the danger of not properly implementing the algorithm is that it will be extremely slow. Try your algorithm on 10**6 for example.
Shortest implementation of the bounded Sieve of Eratosthenes I can come up with:
def primes(upTo):
isPrime = list(range(upTo))
for p in range(2,int(upTo**0.5)+1): #p: 2,3,4,...,sqrt(N)
print(p, isPrime[p])
if isPrime[p]:
for multiple in range(p**2,upTo,p): #mult: p^2, p^2+p, p^2+2p, ..., N
isPrime[multiple] = False
return [x for x in isPrime[2:] if x]
Demo:
>>> list(primes(29))
[2, 3, 5, 7, 11, 13, 17, 19, 23]
It's actually rather succinct, if you ignore linebreaks and the massive skip-even-numbers optimization:
isPrime=[True]*upTo for p in range(2,upTo): if isPrime[p]: yield p for m in range(p,upTo,p): isPrime[m]=False
Here is the most compact true sieve I have come up with so far. This performs surprisingly well.
def pgen(n): # Sieve of Eratosthenes generator
np = set() # keeps track of composite (not prime) numbers
for q in xrange(2, n+1):
if q not in np:
yield q
np.update(range(q*q, n+1, q))
>>> list(pgen(100))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
79, 83, 89, 97]
This slightly more complex version is the fastest I have seen:
def pgen(n): # Sieve of Eratosthenes generator by Dan Salmonsen
yield 2
np = set()
for q in xrange(3, n+1, 2):
if q not in np:
yield q
np.update(range(q*q, n+1, q+q))
Here is a true sieve as a list comprehension:
def primes(n):
sieve = set(sum([range(q*q, n+1, q+q) for q in odds], []))
return [2] + [p for p in odds if p not in sieve]
>>> primes(100)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
79, 83, 89, 97]
The following one-liner is not related at all to your code:
def primes(n):
return set(range(2,n))-{c for i in range(2,n) for c in range(2*i,n,i)}
Like your code, this is still not really the Sieve of Eratosthenes because, for example, it will futilely try to cross off multiples of 6 and 9 etc. Nevertheless it still runs significantly faster than most other Sieve look-alikes for values less than a million or more, since for small N there are "about as many" primes as non-primes (the fraction of numbers < N that are prime is 1/log(N)).
Heavily modified from source, possibly less efficient than original: http://codeblog.dhananjaynene.com/2011/06/10-python-one-liners-to-impress-your-friends/
Here's a simple demonstration of the sieve. Note that lambda isn't used as the filtering function, because the prime number needs to bound at definition time. Also of interest is that it's efficient in the sense of not duplicating divisions, but in the long run it could lead to you-know-what.
import itertools
def primes():
ints = itertools.count(2)
while True:
p = next(ints)
yield p
ints = itertools.ifilter(p.__rmod__, ints)
print list(itertools.islice(primes(), 10))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
def sieve(n):
sieve_list = range(n)
zero_list = [0] * n
for i in range(2, int(n**.5) + 1):
if sieve_list[i]:
sieve_list[2*i:n:i] = zero_list[2*i:n:i]
return filter(None, sieve_list)[1:]
Not exactly the the most compact solution, but the step argument in the range function in Python3 helps here -
prime_sieve = [True] * (int(input('Primes Upto ?'))+1)
# The first prime number
for i in range(2, len(prime_sieve)):
if prime_sieve[i]:
for j in range(i+i, len(prime_sieve), i):
prime_sieve[j] = False
print(i, end=',')
Related
I have a list of values and I want to select n values in such a way that most of the elements come from the beginning of the list and diminish as it goes further in the list (as shown in the link below).
np.random.seed(0)
a = pd.Series(range(100))
np.random.shuffle(a)
a.values
array([26, 86, 2, 55, 75, 93, 16, 73, 54, 95, 53, 92, 78, 13, 7, 30, 22,
24, 33, 8, 43, 62, 3, 71, 45, 48, 6, 99, 82, 76, 60, 80, 90, 68,
51, 27, 18, 56, 63, 74, 1, 61, 42, 41, 4, 15, 17, 40, 38, 5, 91,
59, 0, 34, 28, 50, 11, 35, 23, 52, 10, 31, 66, 57, 79, 85, 32, 84,
14, 89, 19, 29, 49, 97, 98, 69, 20, 94, 72, 77, 25, 37, 81, 46, 39,
65, 58, 12, 88, 70, 87, 36, 21, 83, 9, 96, 67, 64, 47, 44])
what is the good way to select those numbers?
http://www.bydatabedriven.com/wp-content/uploads/2012/12/Screen-Shot-2012-12-03-at-8.12.36-PM.png
As an example if n = 10, then the returned values may be (more numbers picked from the begining of the list as compared to those values which are to the end of the list) :
26, 2, 16, 92, 8, 45, 61, 99, 94 39
You can use np.random.choice and pass appropriately-shaped weights, either directly or using pd.Series.sample since you're using pandas. For example:
In [59]: s = pd.Series(range(100))
In [60]: chosen = s.sample(10**6, replace=True, weights=1.0/(1+np.arange(len(s)))) #typecast the weight to float
In [61]: chosen.hist(bins=50).get_figure().savefig("out.png")
gives me
You can tweak the weights function to your heart's content. Here I used basically 1/i, so that the 4th element is 4 times less likely to be selected than the first. You could take that expression to some power, with **2 making the 4th element 16 times less likely to be selected, or **0.5 making the 4th element half as likely to be selected as the first. Entirely up to you to find a behaviour you're happy with.
Also note that here I'm using replace=True, because I wanted to select a large number of values to make the plot look better. If you don't want the same element to be selected twice, use replace=False.
Solving by Re-inventing the Wheel
Here is how you do it from the first principles.
random.random() returns a random number between 0 and 1. This means that the expression random.random() < x becomes true less frequently as x becomes closer to 0.
For each element in the array, say array[i], let us define the odds of the element getting picked as
odds_pick(i) = (1 - i / len(array)) * DAMP.
Here, DAMP is a number between 0 and 1, which is used to diminish the odds of a number being picked. Thus,
when i = 0 (the first element), the odds of the element being picked are just = DAMP.
For the last element, it's DAMP / len(array).
For others, the odds are between these extremes, and they diminish as i gets larger.
Finally, to get this appropriate sample, iterate over the array, and check if random.random() < odds_pick(i). If so, pick the element.
Because of how we have defined odds_pick(i), it will become more and more close to 0 as i increases, and thus random.random() < odds_pick(i) will be less and less true towards the end. Ultimately, this means we end up picking elements more frequently from the front than from the end.
Code:
import random
def sample_with_bias(arr, n, damping_factor=.3):
res = []
indexes_picked = []
n_picked = 0
while n_picked < n:
for i, x in enumerate(arr):
odds_pick = damping_factor * (1 - i * 1. / len(arr))
if i not in indexes_picked and random.random() < odds_pick:
print(odds_pick)
n_picked += 1
indexes_picked.append(i)
res.append(x)
return res
Note that there are multiple pass over the array to cover the corner case where n unique elements could not be sampled in a single pass.
Let's run some experiments:
def run_experiment(arr, damping_factor, num_pick=10, num_runs=100):
all_samples = []
for i in range(num_runs):
all_samples.extend(sample_with_bias(arr, num_pick, damping_factor=damping_factor))
dist = Counter(all_samples)
dist = sorted(list(dist.items()), key=lambda k: k[0])
k, v = zip(*dist)
plt.bar(k, v)
plt.title("Damping Factor = {0}".format(damping_factor))
plt.show()
and
for df in [0.10, 0.50, 0.99]:
np.random.seed(0)
a = pd.Series(range(100))
run_experiment(a, damping_factor=df)
Results
For lots of damping, almost uniform with still a bias for the elements in the beginning:
Let's see what happens when we decrease the damping:
With almost no damping, only the elements in the front are picked:
This is a problem from codewars.
Given an integer N, write a function values that finds how many numbers in the interval (1,...,N) are palindromes and can be expressed as a sum of consecutive squares.
For example, values(100) will return 3. In fact, the numbers smaller than 100 that have the above property are:
5 = 1+4,
55 = 1+4+9+16+25
77 = 16+25+36
Palindromes that are perfect squares such as 4, 9, 121, etc do not count. Tests are up to N=10^7.
I can solve the problem and pass all the test cases, but I cannot meet the efficiency requirements (the process is killed after 12s).
Am I simply missing some key observation, or there is something wrong with the efficiency of my code below? I broke it down a bit for better readability.
from numpy import cumsum
def is_palindrome(n):
return str(n) == str(n)[::-1]
def values(n):
limit = int(n**0.5) # could be improved
pals = []
for i in range(1,limit):
# take numbers in the cumsum that are palindromes < n
sums = [p for p in cumsum([i**2 for i in range(i,limit)]) if p<n and is_palindrome(p)]
# remove perfect-squares and those already counted
pals.extend(k for k in sums if not (k in pals) and not (k**0.5).is_integer())
return len(pals)
Note: I'm aware that checking that a number is a perfect square using sqrt(n).is_integer() might not be perfect but should be enough for this case.
Apart from working on computational efficiency, you could improve your algorithm strategy. There is a formula for the sum of all squares s(n)=1²+2²+...+n² = n(n+1)(2n+1)/6. So instead of adding m²+(m+1)²+...+n², you could calculate s(n)-s(m-1). A list for all s(n) to find all possible pairs with itertools and subtract them should speed up your program.
More information about pyramidal numbers
Following #n.m suggestion, you could pre-compute all the values for n<=10e7, and return the number of matches:
import bisect
def value(n):
vals = [5, 55, 77, 181, 313, 434, 505, 545, 595, 636, 818, 1001, 1111, 1441, 1771, 4334, 6446, 17371, 17871, 19691, 21712, 41214, 42924, 44444, 46564, 51015, 65756, 81818, 97679, 99199, 108801, 127721, 137731, 138831, 139931, 148841, 161161, 166661, 171171, 188881, 191191, 363363, 435534, 444444, 485584, 494494, 525525, 554455, 629926, 635536, 646646, 656656, 904409, 923329, 944449, 964469, 972279, 981189, 982289, 1077701, 1224221, 1365631, 1681861, 1690961, 1949491, 1972791, 1992991, 2176712, 2904092, 3015103, 3162613, 3187813, 3242423, 3628263, 4211124, 4338334, 4424244, 4776774, 5090905, 5258525, 5276725, 5367635, 5479745, 5536355, 5588855, 5603065, 5718175, 5824285, 6106016, 6277726, 6523256, 6546456, 6780876, 6831386, 6843486, 6844486, 7355537, 8424248, 9051509, 9072709, 9105019, 9313139, 9334339, 9343439, 9435349, 9563659, 9793979, 9814189, 9838389, 9940499, 10711701, 11122111, 11600611, 11922911, 12888821, 13922931, 15822851, 16399361, 16755761, 16955961, 17488471, 18244281, 18422481, 18699681, 26744762, 32344323, 32611623, 34277243, 37533573, 40211204, 41577514, 43699634, 44366344, 45555554, 45755754, 46433464, 47622674, 49066094, 50244205, 51488415, 52155125, 52344325, 52722725, 53166135, 53211235, 53933935, 55344355, 56722765, 56800865, 57488475, 58366385, 62988926, 63844836, 63866836, 64633646, 66999966, 67233276, 68688686, 69388396, 69722796, 69933996, 72299227, 92800829, 95177159, 95544559, 97299279]
return bisect.bisect_right(vals, n)
You get into the realm of a few hundred nanoseconds...
Some mistakes in your code:
1 - You need to use p <= n instead of p < n ( try n = 77 )
2 - Use set instead of list to store your answer, that will speed up your solution.
3 - You don't need to call cumsum in the range [i, limit) you can accumulate until the sum gets greater than n. This will speed up your solution too.
4 - Try to be less "pythonic". Do not fill your code of long and ugly list comprehensions. (This is not a mistake exactly)
This is your code after some changes:
def is_palindrome(s):
return s == s[::-1]
def values(n):
squares = [0]
i = 1
while i * i <= n:
squares.append(squares[-1] + i * i)
i += 1
pals = set()
for i in range(1, len(squares)):
j = i + 1
while j < len(squares) and (squares[j] - squares[i - 1]) <= n:
s = squares[j] - squares[i - 1]
if is_palindrome(str(s)):
pals.add(s)
j += 1
return len(pals)
you can use some itertools tricks to likely speed this up a bit.
import itertools
limit = int(n**0.5) # as before, this can be improved but non-obviously.
squares = [i**2 for i in range(1, limit+1)]
num_squares = len(squares) # inline this to save on lookups
seqs = [(squares[i:j] for j in range(i+2, num_squares)) for i in range(num_squares-2)]
seqs is now a list of generators that build your square sequences. e.g. for n=100 we have:
[ [[1, 4], [1, 4, 9], [1, 4, 9, 16], [1, 4, 9, 16, 25], [1, 4, 9, 16, 25, 36], [1, 4, 9, 16, 25, 36, 49], [1, 4, 9, 16, 25, 36, 49, 64], [1, 4, 9, 16, 25, 36, 49, 64, 81]],
[[4, 9], [4, 9, 16], [4, 9, 16, 25], [4, 9, 16, 25, 36], [4, 9, 16, 25, 36, 49], [4, 9, 16, 25, 36, 49, 64], [4, 9, 16, 25, 36, 49, 64, 81]],
[[9, 16], [9, 16, 25], [9, 16, 25, 36], [9, 16, 25, 36, 49], [9, 16, 25, 36, 49, 64], [9, 16, 25, 36, 49, 64, 81]],
[[16, 25], [16, 25, 36], [16, 25, 36, 49], [16, 25, 36, 49, 64], [16, 25, 36, 49, 64, 81]],
[[25, 36], [25, 36, 49], [25, 36, 49, 64], [25, 36, 49, 64, 81]],
[[36, 49], [36, 49, 64], [36, 49, 64, 81]],
[[49, 64], [49, 64, 81]],
[[64, 81]],
]
If we map sum over those we can use itertools.takewhile to cut down on the number of equality checks we need to do later:
sums = [itertools.takewhile(lambda s: s <= n, lst) for lst in [map(sum, lst) for lst in seqs]]
This cuts down the resulting list substantially, while tallying the accumulated sums
[ [5, 14, 30, 55, 91],
[13, 29, 54, 90],
[25, 50, 86],
[41, 77],
[61],
[85],
[],
[],
]
We can cut out those empty lists with filter(None, sums), then chain together with itertools.chain.from_iterable and pass into is_palindrome.
def is_palindrome(number):
s = str(number)
return s == s[::-1]
result = [k for k in itertools.chain.from_iterable(filter(None, sums)) if is_palindrome(k)]
We could do our perfect square check here, too, but we already know that any perfect square must be in squares. For arbitrarily large n, it becomes cheaper and cheaper to build these both into sets and use set.difference.
result = {k for k in ...} # otherwise the same, just use curly braces
# to invoke set comprehension instead of list
squareset = set(squares)
final = result.difference(squareset)
# equivalent to `result - squareset`
A lot of those sites use problems that are relatively easy to solve programatically, but hard to do efficiently. This will be a common problem you run into.
As for a solution, first try to come up with an efficient algorithm. Then, if it doesn't satisfy the time constraint, work on implementing finding less computationally expensing python standard library methods that achieve the same thing. For example, does pals.extend() traverse the entire list every time, or is there a pointer to the last element? If it traverses, then look for a method that doesn't (pals.append() might do this, but I'm not sure
I'm trying to filter off the prime numbers from 1 to 100 and here is the codes. However, it turns out that there are many numbers missed in the output.
def isnot_prime(x):
if x == 1:
return True
if x == 2:
return False
for i in range(2, int(x**0.5)+1):
if x % i == 0:
return True
else:
return False
print filter(isnot_prime, range(1,101))
The output is [1, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100].
There must be something wrong with the algorithm. How can I improve it?
Thank you.
Modify your for to this:
for i in range(2, int(round(x**0.5 + 1))+1):
if x % i == 0:
return True
Remove the else: and remember that int(float) just takes the integral part (it does not round).
Also, keep in mind that there are faster algorithms to do this. For example the Sieve of Eratosthenes is a fast and simple algorithm.
I would do it this way :
print filter(lambda x: len(['' for y in range(2,x) if x%y==0])==0,range(1,101) )
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Fastest way to list all primes below N in python
I have not been doing programming for very long, and I'm just doing this for fun, and I don't know much advanced Python, but...
I wrote this, and I wanted to know whether it is actually an Eratosthenes Sieve program, and if it is, how could I make it faster. I don't really want someone to post a program that is a solution, but more tell me how I could adapt mine.
def eratSieve(n):
all = []
for a in range(2, n+1):
all.append(a)
for b in all:
for i in range(2,int(round(len(all)/b))):
while i*b in all:
all.remove(i*b)
i+=1
return all
Thanks for your help.
BTW - It's in Python 2.7
It does not work right.
The main problem is that you loop on all the value in all and in the while you remove some element from all.
This way some value in all are not considered, so the function does not remove all the non-prime numbers
Try to execute it for n=100 and the result you get is
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99
while it should be
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
(from http://en.wikipedia.org/wiki/Prime_number)
Also, the range of the second for is wrong, since you consider the lenght of the list, not the current value of b and so you check for multiple of 2 only in the first 50 values, the multiple of 3 in the first 17, 5 in the first 9 and so on. From b = 13 you never enter in the inner for, since int(round(len(all)/b)) = 1 and so you have something like for i in range(2,1)
I agree with Gianluca, and I have a possible solution: keep your main array (all) as bools and mark non-primes, but don't remove them. It might also be faster, because you don't change list size.
Minor thing: you can just write all = range(2, n+1) in the beginning if you want to keep it as ints.
Your method produces incorrect results. The error lies in the for i loop. Here it is, adjusted, and with a test:
known_primes = [
2,3,5,7,11,13,17,19,23,29,
31,37,41,43,47,53,59,61,67,71,
73,79,83,89,97,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,
233,239,241,251,257,263,269,271,277,281,
283,293,307,311,313,317,331,337,347,349,
353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,
467,479,487,491,499,503,509,521,523,541,
547,557,563,569,571,577,587,593,599,601,
607,613,617,619,631,641,643,647,653,659,
661,673,677,683,691,701,709,719,727,733,
739,743,751,757,761,769,773,787,797,809,
811,821,823,827,829,839,853,857,859,863,
877,881,883,887,907,911,919,929,937,941,
947,953,967,971,977,983,991,997,1009,1013,
1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,
1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,
1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,
1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,
1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,
1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,
1453,1459,1471,1481,1483,1487,1489,1493,1499]
def eratSieve(n):
all = []
for a in range(2, n+1):
all.append(a)
for b in all:
for i in all[all.index(b):]:
while i*b in all:
all.remove(i*b)
i+=1
return all
for N in range(1500):
for n in eratSieve(N):
if n not in known_primes:
print N,n
def primes(N):
primes = [x for x in (2, 3, 5, 7, 11, 13) if x < N]
if N < 17: return primes
candidators = [x for x in xrange((N - 2) | 1, 15, -2)
if x % 3 and x % 5 and x % 7 and x % 11 and x % 13]
top = int(N ** 0.5)
while (top + 1) * (top + 1) <= N: top += 1
while True:
p = candidators.pop()
primes.append(p)
if p > top: break
candidators = filter(p.__rmod__, candidators)
candidators.reverse()
primes.extend(candidators)
return primes
I think this code would work faster...
In many languages we can do something like:
for (int i = 0; i < value; i++)
{
if (condition)
{
i += 10;
}
}
How can I do the same in Python? The following (of course) does not work:
for i in xrange(value):
if condition:
i += 10
I could do something like this:
i = 0
while i < value:
if condition:
i += 10
i += 1
but I'm wondering if there is a more elegant (pythonic?) way of doing this in Python.
Use continue.
for i in xrange(value):
if condition:
continue
If you want to force your iterable to skip forwards, you must call .next().
>>> iterable = iter(xrange(100))
>>> for i in iterable:
... if i % 10 == 0:
... [iterable.next() for x in range(10)]
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
As you can see, this is disgusting.
Create the iterable before the loop.
Skip one by using next on the iterator
it = iter(xrange(value))
for i in it:
if condition:
i = next(it)
Skip many by using itertools or recipes based on ideas from itertools.
itertools.dropwhile()
it = iter(xrange(value))
for i in it:
if x<5:
i = dropwhile(lambda x: x<5, it)
Take a read through the itertools page, it shows some very common uses of working with iterators.
itertools islice
it = islice(xrange(value), 10)
for i in it:
...do stuff with i...
It's a very old question, but I find the accepted answer is not totally stisfactory:
first, after the if ... / [next()...] sequence, the value of i hasn't changed. In your first example, it has.
second, the list comprehension is used to produce a side-effect. This should be avoided.
third, there might be a faster way to achieve this.
Using a modified version of consume in itertools recipes, you can write:
import itertools
def consume(it, n):
return next(itertools.islice(it, n-1, n), None)
it = iter(range(20))
for i in it:
print(i, end='->')
if i%4 == 0:
i = consume(it, 5)
print(i)
As written in the doctstring of consume, the iterator is consumed at C speed (didn't benchmark though). Output:
0->5
6->6
7->7
8->13
14->14
15->15
16->None
With a minor modification, one can get 21 instead of None, but I think this isnot a good idea because this code does work with any iterable (otherwise one would prefer the while version):
import string
it = iter(string.ascii_lowercase) # a-z
for x in it:
print(x, end="->")
if x in set('aeiouy'):
x = consume(it, 2) # skip the two letters after the vowel
print(x)
Output:
a->c
d->d
e->g
h->h
i->k
l->l
m->m
n->n
o->q
r->r
s->s
t->t
u->w
x->x
y->None
Itertools has a recommended way to do this: https://docs.python.org/3.7/library/itertools.html#itertools-recipes
import collections
def tail(n, iterable):
"Return an iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))
Now you can do:
for i in tail(5, range(10)):
print(i)
to get
5
6
7
8
9
I am hoping I am not answering this wrong... but this is the simplest way I have come across:
for x in range(0,10,2):
print x
output should be something like this:
0
2
4
6
8
The 2 in the range parameter's is the jump value
Does a generator function here is rebundant?
Like this:
def filterRange(range, condition):
x = 0
while x < range:
x = (x+10) if condition(x) else (x + 1)
yield x
if __name__ == "__main__":
for i in filterRange(100, lambda x: x > 2):
print i
There are a few ways to create iterators, but the custom iterator class is the most extensible:
class skip_if: # skip_if(object) for python2
"""
iterates through iterable, calling skipper with each value
if skipper returns a positive integer, that many values are
skipped
"""
def __init__(self, iterable, skipper):
self.it = iter(iterable)
self.skip = skipper
def __iter__(self):
return self
def __next__(self): # def next(self): for python2
value = next(self.it)
for _ in range(self.skip(value)):
next(self.it, None)
return value
and in use:
>>> for i in skip_if(range(1,100), lambda n: 10 if not n%10 else 0):
... print(i, end=', ')
...
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
I think you have to use a while loop for this...for loop loops over an iterable..and you cannot skip next item like how you want to do it here