我有一个家庭作业要编写一个素数生成器

I have a homework assignment to program a prime number generator

关于求助的作业规则如下: 如果我对自己的代码感到困惑,并且无法复制和粘贴其他人的代码,我可以寻求帮助。我必须了解所有代码,以及每个功能和代码片段的作用。我们了解了 next 函数,我们必须使用 class 来制作生成器。我们还了解了 StopIteration,但我什么时候使用它呢?当我使用它时,我的代码似乎无法正常工作。可能是我放错地方了。

我在结束生成器时遇到了一些麻烦,当数字不是质数时它也一直返回 None,当我希望它继续运行而不是下一个质数时。我怎样才能让它结束,并且一直持续到它到达下一个素数而不是返回 None? 这是我到目前为止尝试过的:

    class PrimeGenerator:
        def __init__(self, maximum):
            self.maximum = maximum + 1
            self.count = 0
    
        def __next__(self):
            if self.count < self.maximum:
                for num in range(self.count, self.maximum):
                    for divisor in range(2, self.count + 1):
                        if num // divisor != num / divisor:
                            print(f'number is prime, it is {num} and the count is {self.count}, and the divisor is {divisor}')
                            self.count += 1
                            return num
                        else:
                            break
    
                    self.count += 1
    
    my_gen = PrimeGenerator(7)
    print(next(my_gen))
    print(next(my_gen))
    print(next(my_gen))
    print(next(my_gen))
    print(next(my_gen))

如有任何帮助,我们将不胜感激。

您实现的是“迭代器”,而不是真正的“生成器”。根据 Python 文档:

Python provides generator functions as a convenient shortcut to building iterators. Generator functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop.

def PrimeGenerator(maximum):
    for num in range(2, maximum + 1):
        for divisor in range(2, num):
            if num // divisor == num / divisor:
                break
        else:
            yield num


for p in PrimeGenerator(7):
    print(p)

结果:

2
3
5
7

为了完整起见,并展示使用真正的生成器可以节省多少代码,这里是与经典迭代器相同的逻辑:

class PrimeGenerator:

    def __init__(self, highest):
        self.next_candidate = 2
        self.highest_number = highest

    def __iter__(self):
        return self

    def __next__(self):
        for num in range(self.next_candidate, self.highest_number + 1):
            for divisor in range(2, num // 2 + 1):
                if num // divisor == num / divisor:
                    break
            else:
                self.next_candidate = num + 1
                return num
        raise StopIteration

    def next(self):
        return self.__next__()

for p in PrimeGenerator(7):
    print(p)

为了确保它有效,我还尝试使用在迭代器上调用 next() 的模式。这给出了相同的结果:

p = PrimeGenerator(7)
while True:
    try:
        print(p.next())
    except StopIteration:
        break

在class的构造函数中,定义当前数和最大数。拥有最高数的意义在于,当我们遇到一个大于最高数的数时,我们就结束了我们的素数生成。为此,我们提出了一个 StopIteration 异常。

__next__方法中,我们检查数字是否为质数。如果是,我们 return 否则我们前进到下一个并重复该过程。

def is_prime(n):
    if n == 2:
        return True
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True


class PrimeGenerator:
    def __init__(self, highest=100):
        self.current = 2
        self.highest_number = highest

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            if self.current > self.highest_number:
                raise StopIteration
            if is_prime(self.current):
                self.current += 1
                return self.current - 1
            self.current += 1


prime_generator = PrimeGenerator(10)
print(next(prime_generator))
print(next(prime_generator))
print(next(prime_generator))
print(next(prime_generator))
print(next(prime_generator))

# For large maximum values
for prime in PrimeGenerator(100):
    print(prime)

为了停止生成器,您所要做的就是在函数末尾引发 StopIteration。

我认为您的代码的素数查找逻辑是错误的,因此我编写了自己的素数测试。每次调用 __next__ 时,它都会继续测试 count 中的整数,如果它们是质数则返回。我们只需要循环到 sqrt(num) 来检查它是否是素数,因为任何高于 sqrt(num) 的因子都会有一个更小的对应因子。您还可以使用模运算符轻松检查因子,而不是除法。

我还添加了 __iter__ 以便生成器可以在 for 循环中使用。

class PrimeGenerator:
    def __init__(self, maximum):
        self.maximum = maximum
        self.count = 2

    def __iter__(self):
        return self

    def __next__(self):
        while self.count <= self.maximum:
            num = self.count
            is_prime = True
            for divisor in range(2, 1 + int(num ** 0.5)):
                if self.count % divisor == 0:
                    is_prime = False
                    break
            if is_prime:
                self.count += 1
                return num
            self.count += 1
        raise StopIteration()

您的问题和随后的评论让您不清楚 maximum 的含义。 IE。素数生成器应该产生的素数,或者它应该考虑的最大素数。首先让我们考虑它限制了产生的素数数量

class PrimeGenerator:
    def __init__(self, maximum):
        self.maximum = maximum  # how many primes we'll generate
        self.count = 0  # how many primes we've generated
        self.number = 1  # prime the prime pump

    def __iter__(self):
        return self

    def __next__(self):
        if self.count < self.maximum:
            if self.count == 0:  # for speed, treat 2 as a special case
                self.count += 1
                return 2

            while True:
                self.number += 2

                for divisor in range(3, int(self.number ** 0.5) + 1, 2):
                    if self.number % divisor == 0:
                        break
                else:  # no break 'for'
                    self.count += 1
                    return self.number  # found a prime

        raise StopIteration()  # exceeded maximum

if __name__ == '__main__':

    generator = PrimeGenerator(25)

    while True:
        try:
            print(next(generator), end=" ")

        except StopIteration:
            print()
            break

现在让我们修改代码,使 maximum 参数代表考虑的 最大 数字:

class PrimeGenerator:
    def __init__(self, maximum):
        self.maximum = maximum  # largest number we'll consider
        self.number = 2  # prime the prime pump

    def __iter__(self):
        return self

    def __next__(self):
        if self.number <= self.maximum:
            if self.number == 2:  # for speed, treat 2 as a special case
                self.number += 1
                return 2

            while self.number < self.maximum:

                for divisor in range(3, int(self.number ** 0.5) + 1, 2):
                    if self.number % divisor == 0:
                        break
                else:  # no break 'for'
                    prime = self.number
                    self.number += 2
                    return prime  # found a prime

                self.number += 2

        raise StopIteration()  # exceeded maximum

这些示例将 2 视为特例,随后像其他示例一样避免将偶数作为候选 and/or 除数。 (这样做的好处是速度提高了约 4 倍。)