6

楽しい練習として、1 行の Python で素数ジェネレーターを作成しようとしています。

次のコードは期待どおりに動作しますが、遅すぎます。

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,

そこで、私は j と 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,

しかし、それは出力します:2 3 5 6 7 8

したがって、インデックス j と k に何か問題があるに違いありませんが、手がかりがありません。

4

12 に答える 12

13

エラトステネスのふるいのように見えますが、そうではありません。実際にはもっと悪いです。Sieve は、素数を見つけるための最適なアルゴリズムです。

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenesを参照してください。

編集: https://stackoverflow.com/a/9302299/711085をワンライナーに変更しました (元々は本物のふるいではありませんでしたが、現在は...おそらく...):

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

デモ:

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

悲しいことに、これは関数型プログラミング言語では効率的ですが、非永続的な (共有状態で不変の) データ構造のため、Python ではそれほど効率的ではない可能性があり、Python のふるいは突然変異を使用して達成する必要があると思います同等の性能。どうしてもやりたい場合は、ワンライナーに詰め込むことができます。でもまず...

通常ふるい:

>>> 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]

同じ行で無名関数を定義して呼び出すことができるようになりました[...].__setitem__。また、インライン ミューテーションを実行するためのハックと、戻りながら... and foo評価するためのハックもできます。...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]

恐怖に身を縮めると、ワンライナーが展開されます(制御フローをほぼ直接変換できるため、奇妙に美しいですが、すべてのひどい悪用です):

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)))

このワンライナーの変更バージョンは、私のマシンでは約 10 8で放棄されましたが、元の変更バージョンは約 10 9で放棄され、メモリが不足しました (奇妙なことに)。

元のバージョンは 10 7reduceであきらめました。したがって、おそらくそれほど効率が悪いわけではありません(少なくとも、コンピューターで処理できる数値については)。

edit2 次のように、副作用をより簡潔に悪用できるようです。

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)))

10 8付近であきらめます。これは、ワンライナー変異バージョンと同じです。

edit3:これO(N)経験的複雑さdifference_updateで実行されが、それがなければO(n^2.2) 複雑さで実行されます。

削減される範囲を上限の sqrt に制限し、オッズのみで作業すると、どちらも追加のスピードアップをもたらします (対応する2 倍1.6倍):

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)))
于 2012-05-17T16:52:00.307 に答える
3

素数をテストするために平方根までの数の積をチェックすることはできません。8を見てください。8の平方根は2.8なので、4 * 2を試すことはありません(実際、素数として表示されないのは平方数だけです)

ETA:jとkのすべての可能な組み合わせを試す代わりに、iがi % j == 0jの平方根までの各jで(を使用して)除算できるかどうかを確認してみませんか?これにより、必要なコードが少なくなり、効率が大幅に向上します(ただし、エラトステネスのふるいほど効率的ではありません)。

于 2012-05-17T16:46:36.647 に答える
3

これがあなたが欲しかったものです:

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))])

Haskell では、範囲は包括的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)]]] 

実際には、乗数として1は必要ないので1*x == x

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

わずか 0.59 秒かかります。または、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))])

更新:何らかの理由で、min j ...少なくとも Haskell では大きな違いはありません。だから表現は単純になる

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..n`div`j]]] 
于 2012-05-19T10:10:50.003 に答える
3

どうですか:

def primes(x):
  return [i for i in range(2,x) if 0 not in [i%j for j in range(2,i)]]
于 2017-07-23T04:56:02.817 に答える
1

内包表記の使用

[x for x in range(4, 1000) if all(x % y != 0 for y in range(2, int(math.sqrt(x)) + 1))]
于 2019-04-28T09:46:12.773 に答える
0

次のコードは、値 'limit' を下回るすべての数値のリストを出力する必要があります。

primes = lambda limit: [num for num in range(2, limit) if num > 1 and (num == 2 or num % 2 != 0) and all(num % divisor != 0 for divisor in range(3, int(num ** 0.5) + 1, 2))]

print(primes(30))
于 2021-12-27T16:55:54.057 に答える
-1

私のコードは(範囲(2,50)の数値の場合)です:

import operator

[len(x) for x in list(map(lambda x: [operator.mod(len(range(1,x)), z) for z in range(1,x)], [item for item in range(2,50)])) if x.count(0) == 2]
于 2021-05-09T18:24:04.573 に答える