2012-05-17 18 views
5

Tôi đang cố gắng tạo trình tạo số nguyên tố trong một dòng Python giống như một bài tập thú vị.Trình tạo thủ tố Python trong một dòng

Các mã sau đây hoạt động như mong đợi, nhưng nó là quá chậm:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)]) 
for i in primes(10): 
    print i, 

Vì vậy, II đã cố gắng để làm điều đó bằng cách chỉ kiểm tra lên đến vuông gốc của j và k:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,int(round(math.sqrt(i)+1))) for k in xrange(1,int(round(math.sqrt(i)+1)))]) 
for i in primes(10): 
    print i, 

Nhưng kết quả đầu ra: 2 3 5 6 7 8

Vì vậy, phải có điều gì đó sai với chỉ số j và k của tôi, nhưng tôi không có đầu mối.

+0

Nếu bạn có thể sống với một tổ chức phi oneliner, có câu hỏi này: http://stackoverflow.com/questions/567222/simple- máy phát điện nguyên tố-in-python – Andy

+0

có thể trùng lặp của [Python-Sieve of Eratosthenes- Compact Python] (http://stackoverflow.com/questions/6687296/python-sieve-of-eratosthenes-compact-python) – ninjagecko

+2

Tôi đã có thể để làm điều đó trong hai dòng: http://stackoverflow.com/a/9302299/5987 –

Trả lời

11

Đó không phải là Sàng của Eratosthenes, mặc dù nó trông giống như vậy. Đó là trong thực tế tồi tệ hơn nhiều. Sàng là thuật toán tốt nhất để tìm số nguyên tố.

Xem http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

chỉnh sửa: Tôi đã sửa đổi https://stackoverflow.com/a/9302299/711085 là một one-liner (ban đầu nó không thực sự Sieve, nhưng bây giờ nó là ... có lẽ ...):

reduce((lambda r,x: r-set(range(x**2,N,x)) if (x in r) else r), 
     range(2,N), set(range(2,N))) 

Demo:

>>> primesUpTo(N): lambda N: reduce(...) 
>>> primesUpTo(30) 
{2, 3, 5, 7, 11, 13, 17, 19} 

Đáng buồn là tôi nghĩ rằng mặc dù điều này có hiệu quả trong ngôn ngữ lập trình hàm, nhưng nó có thể không hiệu quả trong trăn do cấu trúc dữ liệu không liên tục (chia sẻ trạng thái và bất biến), và bất kỳ rây nào trong python sẽ cần phải sử dụng đột biến đạt được hiệu suất tương đương. Chúng ta vẫn có thể nhồi nhét nó thành một lớp lót nếu chúng ta muốn tuyệt vọng. Nhưng trước tiên...

sàng bình thường:

>>> N = 100 
>>> table = list(range(N)) 
>>> for i in range(2,int(N**0.5)+1): 
...  if table[i]: 
...   for mult in range(i**2,N,i): 
...    table[mult] = False 
... 
>>> primes = [p for p in table if p][1:] 
>>> 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] 

Chúng tôi bây giờ có thể xác định và gọi chức năng ẩn danh trên cùng một dòng, cũng như việc hack của [...].__setitem__ để làm đột biến nội tuyến, và hack của ... and foo để đánh giá ... khi trở về foo :

>>> primesUpTo = lambda N: (lambda table: [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] for i in range(2,int(N**0.5)+1) if table[i]] and [p for p in table if p][1:])(list(range(N))) 
>>> primesUpTo(30) 
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 

tiếp tục co rúm người trong nỗi kinh hoàng, the one-liner mở rộng (kỳ quặc đẹp bởi vì bạn có thể gần như trực tiếp dịch kiểm soát dòng chảy, nhưng một sự lạm dụng khủng khiếp của tất cả mọi thứ):

lambda N: 
    (lambda table: 
     [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] 
      for i in range(2,int(N**0.5)+1) if table[i]] 
     and [p for p in table if p][1:] 
    )(list(range(N))) 

phiên bản đột biến một lót này đã từ bỏ vào khoảng 10 trên máy tính của tôi, trong khi phiên bản mutating gốc đã từ bỏ vào khoảng 10 , chạy ra khỏi bộ nhớ (kỳ quặc).

Bản gốc reduce phiên bản đã hết tại 10 . Vì vậy, có lẽ nó không phải là rằng không hiệu quả sau khi tất cả (ít nhất là cho số bạn có thể đối phó với trên máy tính của bạn).

edit2 Có vẻ như bạn có thể lạm dụng tác dụng phụ ngắn gọn hơn như:

reduce((lambda r,x: (r.difference_update(range(x**2,N,x)) or r) 
        if (x in r) else r), 
     range(2,N), set(range(2,N))) 

Nó cung cấp cho lên khoảng 10 , giống như các phiên bản đột biến một lót.

EDIT3: này runs at O (N) empirical complexity, trong khi nếu không có sự difference_update nó chạy ở O(n^2.2) complexity.

Hạn chế phạm vi đó được giảm xuống kết thúc, đến sqrt của giới hạn trên, và làm việc with odds only, cả hai kết quả trong thêm tốc độ-up (2x1.6x tương ứng):

reduce((lambda r,x: (r.difference_update(range(x*x,N,2*x)) or r) 
        if (x in r) else r), 
     range(3, int((N+1)**0.5+1), 2), 
     set([2] + range(3,N,2))) 
+0

Đúng, nhưng điều này không giải quyết lỗi trong một lót của mình. –

+0

@DavidRobinson: ông dường như rõ ràng hơn rất nhiều về tốc độ câu trả lời của mình, có liên quan trực tiếp đến thuật toán. Thuật toán trông giống như sẽ bắt đầu cảm thấy chậm chạp ở mức 10000+ và thất bại ở mức 1000000+. Dù sao, tôi đã kể từ khi chỉnh sửa câu trả lời của tôi để cung cấp cho một lớp lót thực sự. – ninjagecko

+1

(nếu có ai đó tự hỏi tại sao tôi tiếp tục chỉnh sửa/unediting, đó là bởi vì nó rất khó để nói nếu một thực hiện không chính thống của Sieve thực sự có một thời gian chạy của http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Algorithm_complexity; thậm chí bây giờ chắc chắn ... đáng buồn tôi nghĩ rằng trong khi điều này sẽ hiệu quả trong một ngôn ngữ lập trình chức năng, nó không hiệu quả trong trăn do cấu trúc dữ liệu không chia sẻ, và bất kỳ sàng trong python sẽ cần phải sử dụng đột biến) – ninjagecko

4

Bạn không thể kiểm tra sản phẩm của các số chỉ đến căn bậc hai để kiểm tra giá trị nguyên tố. Nhìn vào 8- căn bậc hai của 8 là 2,8, vì vậy nó sẽ không bao giờ thử 4 * 2. (Thật vậy, những con số duy nhất mà sẽ không được xem là số nguyên tố là số vuông).

ETA: Thay vì thử tất cả các kết hợp có thể có của j và k, tại sao không kiểm tra xem tôi có chia hết cho mỗi j (sử dụng i % j == 0) tối đa căn bậc hai của j không? Điều này cả hai mất ít mã và hiệu quả hơn nhiều (mặc dù nó vẫn là không gần như hiệu quả như Sàng Eratosthenes).

2

Dưới đây là những gì bạn muốn:

def primes (q) : 
# return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)]) 
# return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,j+1)]) 
# return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,j+1)]) 

return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,min(j+1,i/j+1))]) 

Trong Haskell, phạm vi bao gồm, vì vậy primes(542)

[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],  k<-[1..n-1]]] -- 25.66s 
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],  k<-[1..j]]] -- 15.30s 
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..j]]] -- 6.00s 
                     -- 0.79s 
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..min j (n`div`j)]]] 

Và trên thực tế, 1*x == x nên là không cần thiết như một số nhân, do đó nó phải được

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..min j (n`div`j)]]] 

mà chỉ mất 0,59 giây.Hoặc, trong Python,

def primes (q) : 
return (i for i in xrange(2,q) if i not in [j*k for j in xrange(2,i/2+1) for k in xrange(2,min(j+1,i/j+1))]) 

update: đối với một số lý do, min j ... không làm cho nhiều sự khác biệt, trong Haskell ít nhất. Vì vậy, sự biểu hiện trở nên đơn giản

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..n`div`j]]] 
-1

Làm thế nào về:

def primes(x): 
    return [i for i in range(2,x) if 0 not in [i%j for j in range(2,i)]] 
+0

Bạn có thể viết lại ngắn hơn một chút là: số nguyên tố (x): trả về [i cho i trong phạm vi (2, x) nếu tất cả ([i% j cho j trong phạm vi (2, i)])] – Eitan

Các vấn đề liên quan