Объясняя эту функцию генератора простых чисел, я не могу понять [python] - PullRequest
2 голосов
/ 08 июня 2011
def primes(n): 
    if n==2: return [2]
    elif n<2: return []
    s=range(3,n+1,2)
    mroot = n ** 0.5
    half=(n+1)/2-1
    i=0
    m=3
    while m <= mroot:
        if s[i]:
            j=(m*m-3)/2
            s[j]=0
            while j<half:
                s[j]=0
                j+=m
        i=i+1
        m=2*i+3
    return [2]+[x for x in s if x]

Здравствуйте, я нашел эту функцию здесь http://code.activestate.com/recipes/366178-a-fast-prime-number-list-generator/, и я застрял.Я не понимаю этого.Я думаю, что он использует некоторые свойства простых чисел, но все эти однобуквенные переменные настолько загадочны.Не могли бы вы пролить немного света?

Это я понимаю: mroot - это предел чисел, которые вы хотите проверить на простоту, и я знаю, что функция меняет список s на 0, отмечая кратные числа.Я также понимаю, что список в конце понятен, и я понимаю s.

Но почему половина?Что такое j?Что м?

Не могли бы вы дать комментарий?

Ответы [ 3 ]

5 голосов
/ 08 июня 2011

Построчная разбивка:

def primes(n): 
    if n==2: return [2]

Эта функция возвращает список простых чисел <= n. Так что если n == 2, этот список содержит только 2. Достаточно просто.

    elif n<2: return []

Здесь снова нет простых чисел ниже 2, поэтому верните пустой список.

    s=range(3,n+1,2)

Итак, s - это список нечетных чисел, начиная с 3 и заканчивая n + 1. s обозначает сито - это алгоритм сита, который означает, что составные (не простые) числа будут исключены из списка. По сути, они будут «перечеркнуты». Ниже приведено подробное описание алгоритмов сита.

    mroot = n ** 0.5

Поскольку это сито, мы можем остановить алгоритм, как только достигнем квадратного корня из n.

    half=(n+1)/2-1

Это явная формула для длины s; его можно заменить на len(s), но это может занять больше времени для расчета при больших значениях n. Это также будет полезно для завершения определенных частей алгоритма.

    i=0
    m=3

Я - наш индекс; Я просто перешагиваю через сито, проверяя каждое значение. Если значение 0, то число было «вычеркнуто», потому что оно не простое. m - это просто значение s[i] в любой данный момент; более поздняя строка держит это обновленным.

    while m <= mroot:
        if s[i]:

Поскольку s[i] оценивается как True, он еще не вычеркнут из списка. Это означает, что это главное! Итак, теперь мы должны выяснить, какие числа в списке кратны s[i] - все они не простые числа и должны быть вычеркнуты из списка.

            j=(m*m-3)/2
            s[j]=0

Теперь начинается самое интересное. Поскольку сито - это не список последовательных чисел, а список нечетных чисел, мы должны выяснить, где кратные нашего простого числа живут в s. В этом случае наше простое число равно 3, поэтому нам нужно найти индекс 9, 15, 21, 27 ... (нам не нужно находить 6, 12, 18 ... потому что они даже и так нет в списке). Эта конкретная техника для нахождения индексов действительно умна, потому что автор понял, что после того, как все кратные определенного простого числа были вычеркнуты, они могут быть пропущены. Это означает, что первое не вычеркнутое кратное нашего простого числа на самом деле является квадратом этого простого числа. (Так, например, если бы простое число было 7, 7 * 3 = 21 и 7 * 5 = 35 уже было бы вычеркнуто, поэтому первое кратное число 7, с которым мы должны иметь дело, это 7 * 7.) Как только это делает в смысле, довольно легко увидеть, что расположение 9 в s равно (9 - 3) // 2 (где // - деление по полу).

            while j<half:
                s[j]=0
                j+=m

Теперь все возвращается к простоте. Мы нашли 9; теперь мы должны найти 15 = 9 + 3 + 3. Поскольку s содержит только нечетные числа, это вдвое меньше, чем список с каждым числом; чтобы пропустить 6, нам нужно только добавить 3 к j. Мы делаем это, пока j меньше half - другими словами, пока j меньше длины s.

        i=i+1
        m=2*i+3

Опять же, просто - i - это просто индекс списка, а m - это значение числа, которое было там изначально. (Вы можете проверить это, чтобы понять, почему: [2 * i + 3 for i in range(10)].)

    return [2]+[x for x in s if x]

И вуаля - отфильтруйте нули из сита, добавьте [2] и у вас есть список простых чисел.

Наиболее запутанная вещь в этом алгоритме связана с ярлыками, выбранными автором, которые ускоряют его выполнение, но затуманивают фундаментальную концепцию. (На самом деле, есть еще несколько ярлыков, которые можно использовать, но это другой пост.) Вот гораздо более простое сито, которое показывает основную идею:

>>> numbers = range(40)
>>> numbers[1] = 0    # 1 isn't prime
>>> for i in numbers:
...     if i:
...         for j in range(i + i, len(numbers), i):
...             numbers[j] = 0
... 
>>> [n for n in numbers if n]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]

Чтобы разобраться, первые числа выглядят так:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10...]

Тогда ...

[0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10...]
[0, 0, 2, 3, 0, 5, 0, 7, 0, 9, 0...]
[0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0...]

И так далее.

2 голосов
/ 08 июня 2011

Действительно, он использует несколько трюков.

  • half представляет примерно половину n.Плюс и минус один только для того, чтобы убедиться, что half на самом деле самое большое целое число меньше половины n.
  • m всегда то, что s[i] было в то время, когда списокбыл создан, что также равно 2*i+3.(Вы можете получить это из определения s).m - это всегда текущее простое число, множители которого вы удаляете.
  • j используется для устранения кратных чисел.Он начинается с индекса m в квадрате (поскольку это наименьшее число, которое необходимо вычеркнуть ((m*m-3)/2 отменяет приведенную выше формулу для получения значения, в данном случае m*m, из индекса)
  • Затем цикл while вычеркивает каждый m -й элемент списка, начиная с первого j. Обратите внимание, что поскольку в списке есть только нечетные числа, s[j+m] == s[j]+2m, что нормально, поскольку мы пропускаем четные кратныетаким образом.
  • Затем он просто перебирает i и обновляет m соответственно.
2 голосов
/ 08 июня 2011

Это реализация Сита из Эратосфена . Требуется несколько сочетаний клавиш - например, вместо того, чтобы начинать с «2» и вычеркивать все четные числа, он использует диапазон, чтобы даже не генерировать их (он начинается с 3 и генерирует только каждое второе число - таким образом, 3, 5, 7 и т. д. до n + 1.

...