Лямбда-функция для вычисления суммы простых чисел - PullRequest
2 голосов
/ 09 января 2020

При написании python программы для вычисления суммы простых чисел от 2 до n (включая оба) я получаю сообщение об ошибке «Генератор не вызывается»

Может кто-нибудь помочь с ошибкой или подскажите что мне здесь не хватает?

n = int(input())

sum = 0
sum = sum(list(filter((lambda n: n%y != 0 for y in range(2, n)), range(2, n+1))))

print(sum)

Ответы [ 5 ]

2 голосов
/ 09 января 2020

Что ж, в моем воображении такой подход был бы оптимальным:

  • делится только с (уже) известными простыми числами (а не с каждым числом)
  • до квадрата root данное число

import math

primes=[2]

def isprime(n):         # NB! this is fragile,
  s=int(math.sqrt(n))   #     relies on `primes` being
  for p in primes:      #     already fully grown
    if n%p==0:          #     up to the sqrt(n)
      return False
    if p>s:
      return True

maxn=19
for i in range(3,maxn+1):
  if isprime(i):
    primes.append(i)

print(primes)
print(sum(primes))

Затем наступает увеличение делает его lambda part

isprime=lambda n:all(n%p!=0 for p in primes)

работает, но оно пропускает sqrt magi c, затем

isprime=lambda n:all(n%p!=0 for p in (p for p in primes if p<=int(math.sqrt(n))))

, который вычисляет int(math.sqrt(n)) для всех известных простых чисел, но вместо этого это может быть одноразовый параметр, либо для filter(), либо для внутреннего lambda (этот последний показан здесь):

isprime=lambda n:all(n%p!=0 for p in (lambda s:(p for p in primes if p<=s))(int(math.sqrt(n))))

Мне все еще не нравится тот факт, что он сравнивает все простые числа с s, остановка в середине может быть более эффективной, получение среза с index(next(...)) может сделать это:

isprime=lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))])

просто уродливо, что next() и index() дважды пересекают часть списка.

Затем приходит внешний l oop, я бы используйте reduce() для этого с functools, так как аккумулятором сокращения может быть фактический список простых чисел, и тогда он не будет отдельная переменная.

Шаг 0 сделал бы это минималистичным образом, все еще с переменной primes, лежащей вокруг, но используя ужасный трюк с кортежами, (do,some,things,result)[3] сделает все, и оценит result:

primes=[2]
isprime=lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))])
maxn=19
ps=functools.reduce(lambda primes,n:(primes.append(n) if isprime(n) else None,primes)[1],range(3,maxn+1),primes)
print(ps)
print(primes)
print(sum(primes)) # ps and primes are the same list actually

и затем завершить монстра:

import math
import functools

plist=lambda maxn:functools.reduce(lambda primes,n:(primes.append(n) if (lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))]))(n) else None,primes)[1],range(3,maxn+1),[2])

primes=plist(19)
print(primes)
print(sum(primes))

Тест: https://ideone.com/0y7dN9
Выход:

[2, 3, 5, 7, 11, 13, 17, 19]
77

Выводное сообщение будет таким: лямбды могут быть полезны, но на самом деле вам не всегда нужно их использовать.

2 голосов
/ 09 января 2020

1. Первая ошибка: «TypeError: объект« генератора »не может быть вызван»

эта ошибка возникает из-за того, что вы передаете filter встроенному методу генератор, вы «покрываете» метод lambda с помощью круглые скобки:

filter((lambda n: n%y != 0 for y in range(2,n)), range(2,n+1))

Решение : «раскрыть» скобки

filter(lambda n: n%y != 0 for y in range(2,n), range(2,n+1))

2. вторая ошибка: «SyntaxError: выражение генератора должно быть заключено в скобки» :

lambda n: n%y != 0 for y in range(2,n)

ошибка ясна: n%y != 0 for y in range(2,n) является генератором и его нужно заключить в скобки, здесь вы хотите проверить, если число простое, поэтому вы хотите проверить, все ли значения из вашего генератора равны True

Решение : используйте встроенный метод all

lambda n: all(n%y != 0 for y in range(2,n))

3. последняя ошибка: «Ошибка типа: объект int не вызывается»

sum=0

это происходит потому, что вы используете sum имя встроенного метода для вашей переменной sum, поэтому sum больше не является встроенным методом, а является целым числом

Решение : другое имя вашей переменной:

n_prime_sum = 0

здесь ваш код с исправлениями :

n = int(input())

n_prime_sum = 0
n_prime_sum = sum(list(filter(lambda n: all(n%y != 0 for y in range(2,n)), range(2,n+1))))
n_prime_sum
# n = 10

output:

17

также вы можете определить функцию, которая генерирует все простые числа между 2 и n ( включительно), затем примените встроенный метод sum к этой функции:

# original code by David Eppstein, UC Irvine, 28 Feb 2002
# with comments by Eli Bendersky, https://stackoverflow.com/a/568618

def gen_primes(n):
    """Much more efficient prime generation, the Sieve of Eratosthenes"""

    D = {}

    q = 2   # The running integer that's checked for primeness

    while q <= n:
        if q not in D:
            # q is a new prime.
            # Yield it and mark its first multiple that isn't
            # already marked in previous iterations
            # 
            yield q
            D[q * q] = [q]
        else:
            # q is composite. D[q] is the list of primes that
            # divide it. Since we've reached q, we no longer
            # need it in the map, but we'll mark the next 
            # multiples of its witnesses to prepare for larger
            # numbers
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]

        q += 1

n = int(input())
n_prime_sum = sum(gen_primes(n))
print(n_prime_sum)
# n = 10

вывод:

17

большая часть кода для функции gen_prime взята из здесь

1 голос
/ 09 января 2020

Немного более чистая версия:

n = int(input())
nums = range(2, n + 1)
for i in range(2, n + 1):
    nums = list(filter(lambda x: x == i or x % i, nums))

print(sum(nums))
1 голос
/ 09 января 2020

Вы также можете написать это с пониманием списка:

from math import sqrt, floor

sum(( p 
      for p in range(2, n+1) 
      if all( p%q != 0 for q in range(2, floor(sqrt(p))+1) ) ))

Вам не нужно проверять все числа, только те, которые находятся до квадрата root собственного числа.

Пример: Вы можете узнать, что 10 = 2 * 5 не простое число, проверив 10% 2 == 0 или 10% 5 == 0. С квадратным пределом root вы только проверьте до 3, но это нормально, так как вы уже видели 2 и определили 10 как не простое число.

Если это приложение для вас, вы можете проверить сито Алгоритм Эратосфена для поиска простых чисел, который считается одним из наиболее эффективных способов поиска небольших простых чисел.

Однако, если вам просто нужно найти простые числа и их сложить, вы всегда можете использовать библиотека вроде gmpy2 . Проверьте этот ответ, чтобы узнать больше .

1 голос
/ 09 января 2020

Ваше решение почти правильное. Но вам нужно использовать all внутри лямбда-функции, потому что вы хотите, чтобы все операторы n%y != 0 были истинными:

sum(list(filter((lambda n: all(n%y != 0 for y in range(2,n))), range(2,n+1))))

Как указано в комментариях, вы могли бы повысить эффективность вашего кода, проверяя только делители, которые не больше квадрата root числа:

import math
sum(list(filter((lambda n: all(n%y != 0 for y in range(2,int(math.sqrt(n))+1))), range(2,n+1))))

Пояснение

  • n%y != 0 for y in range(2,n) - это генератор, а не логическое значение!
  • all(n%y != 0 for y in range(2,n)) - это логическое значение, соответствующее n%2 != 0 and n%3 != 0 and ... and n%(n-1) != 0
...