2011-06-07 14 views
5

numaralı telefon çağrısını kullanır. Bu nedenle, for döngüsünde yığınlanmış bazı filtrelerden bazı ilginç davranışlar alıyorum. Ben bir gösteri ile başlayacağız: Yığınlanmış filtrenin tek davranışı()

>>> x = range(100) 
>>> x = filter(lambda n: n % 2 == 0, x) 
>>> x = filter(lambda n: n % 3 == 0, x) 
>>> list(x) 
[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96] 

Burada beklenen çıkışı olsun. Filtrede bir filtre içinde bir aralığa sahibiz ve filtre koşulları istediğimiz gibi istifliyor. Şimdi benim sorunum geliyor.
Bir sayının göreli ilkelerini hesaplamak için bir işlev yazdım. Bu şuna benzer: her ne nedenle

def relative_primes(num): 
    '''Returns a list of relative primes, relative to the given number.''' 
    if num == 1: 
     return [] 
    elif is_prime(num): 
     return list(range(1, num)) 
    result = range(1, num) 
    for factor in prime_factors(num): 
     # Why aren't these filters stacking properly?       
     result = filter(lambda n: n % factor != 0, result) 
    return list(result) 

, filtre yalnızca prime_factors elde listesinde() içinde SON faktörüne uygulanıyor. Örnek:

>>> prime_factors(30) 
[2, 3, 5] 
>>> relative_primes(30) 
[1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29] 

Biz 2 ya da 3 hiçbir katları listesinden çıkarıldı görebiliriz. Bu neden oluyor? Yukarıdaki örnek neden çalışıyor, ancak for döngüsündeki filtreler yok mu?

cevap

7

Python 3.x'te, filter() bir liste yerine bir jeneratör döndürür. Bu nedenle, her üç filtre de aynı factor kullanıyor olduğundan, yalnızca factor son değeri kullanılır. Çalışması için lambda'nızı biraz değiştirmelisiniz.

result = filter(lambda n, factor=factor: n % factor != 0, result) 
+0

Bir başka öznitelik seçeneği 'itiraf etmeliyiz ki, bu kod biraz daha az okunabilir hale getirir. –

1

Yineleyicilerin değerlendirilmesi tembeldir. Tüm filtreler yalnızca O zamana açıklamada

return list(result) 

içinde değerlendirilecektir, factor değeri son asal faktördür. lambda işlevleri, yalnızca factor yerel adına bir başvuru içerir ve yürütme sırasında bu adı ne atanırsa kullanır.

Bunu düzeltmenin bir yolu, her yinelemede bir listeye dönüştürmektir. yerine sadelik performanstan sonra iseniz, ayrıca deneyebilirsiniz bu bir:

def relative_primes(n): 
    sieve = [1] * n 
    for i in range(2, n): 
     if not sieve[i] or n % i: 
      continue 
     sieve[::i] = [0] * (n // i) 
    return list(itertools.compress(range(n), sieve)) 
+0

BU. Tamam, yerel faktör 'faktörü' ile ilgili olabileceğini düşündüm. Bunun için çok teşekkürler. Ayrıca, gcd'leri kullanarak daha iyi bir çözüm biliyorum, ama aslında daha hızlı olması için bu var. Algoritmanın birkaç sürümü var. –

+0

Her şey çalışıyor. Ayrıca, bazı testler yaptım ve algoritmam, teklif numarasına ek bir kontrol eklediğimde bile, önerdiğinizden iki kat daha hızlı. –

+0

@fosskers: Performanstan sonra olduğunu bilmiyordum.Ayrıca hızlı olması gereken başka bir basit uygulama eklendi. –

1

Bir not olarak

, bu işlevin çok daha kolay bir uygulama

from fractions import gcd 
def relative_primes(n): 
    return [i for i in range(1, n) if gcd(n, i) == 1] 

Düzenleme olduğunu Eğer doğru bir şekilde anladım ve İki tam sayı, 1 dışında ortak bir pozitif faktör (bölen) paylaşmazlarsa nispeten daha asaldır. En büyük ortak bölen göstermeyi göstermek için gösterimi kullanma, iki tamsayı a ve b ar gcd (a, b) == 1 ise nispeten prime. sonra fractions modülünü aşağıdaki şekilde kullanabilirsiniz.

from fractions import gcd 

num = 30 
relative_primes = filter(lambda x: gcd(x,num) == 1, xrange(1,num))