由于我开始掌握Python,我开始在projecteuler.net上测试我新获得的Python技能。
无论如何,在某些时候,我最终制作了一个函数,用于获取所有素数的列表直到数字'n'。
以下是该函数的外观:
def primes(n):
"""Returns list of all the primes up until the number n."""
# Gather all potential primes in a list.
primes = range(2, n + 1)
# The first potential prime in the list should be two.
assert primes[0] == 2
# The last potential prime in the list should be n.
assert primes[-1] == n
# 'p' will be the index of the current confirmed prime.
p = 0
# As long as 'p' is within the bounds of the list:
while p < len(primes):
# Set the candidate index 'c' to start right after 'p'.
c = p + 1
# As long as 'c' is within the bounds of the list:
while c < len(primes):
# Check if the candidate is divisible by the prime.
if(primes[c] % primes[p] == 0):
# If it is, it isn't a prime, and should be removed.
primes.pop(c)
# Move on to the next candidate and redo the process.
c = c + 1
# The next integer in the list should now be a prime,
# since it is not divisible by any of the primes before it.
# Thus we can move on to the next prime and redo the process.
p = p + 1
# The list should now only contain primes, and can thus be returned.
return primes
似乎工作得很好,虽然有一件事困扰着我。 在评论代码时,这件作品突然出现了:
# Check if the candidate is divisible by the prime.
if(primes[c] % primes[p] == 0):
# If it is, it isn't a prime, and should be removed from the list.
primes.pop(c)
# Move on to the next candidate and redo the process.
c += 1
如果候选人不能被素数整除,我们会检查位于'c + 1'的下一个候选人。没问题。
但是,如果候选人IS可以被素数整除,我们首先弹出它,然后检查位于'c + 1'的下一个候选者。 让我感到震惊的是,下一个候选人在弹出后不是位于'c + 1',而是'c',因为在'c'弹出后,下一个候选人“落入”该指数。
然后我认为该块应如下所示:
# If the candidate is divisible by the prime:
if(primes[c] % primes[p] == 0):
# If it is, it isn't a prime, and should be removed from the list.
primes.pop(c)
# If not:
else:
# Move on to the next candidate.
c += 1
上面的这个块对我来说似乎更正确,但让我想知道为什么原始作品看起来很好用。
所以,这是我的问题:
在弹出不是素数的候选人之后,我们可以假设,正如我的原始代码中那样,下一个候选人不能被同一个素数整除吗?
如果是这样,为什么会这样?
建议的“安全”代码是否会对那些在“不安全”代码中跳过的候选人进行不必要的检查?
PS:
我尝试将上述假设作为“不安全”函数的断言,并用n = 100000进行测试。没有出现问题。这是修改过的块:
# If the candidate is divisible by the prime:
if(primes[c] % primes[p] == 0):
# If it is, it isn't a prime, and should be removed.
primes.pop(c)
# If c is still within the bounds of the list:
if c < len(primes):
# We assume that the new candidate at 'c' is not divisible by the prime.
assert primes[c] % primes[p] != 0
# Move on to the next candidate and redo the process.
c = c + 1
答案 0 :(得分:11)
它失败了更多的数字。第一个素数是 71 ,因为候选人可能会失败。 71的最小失败候选人是 10986448536829734695346889 ,这使得数字10986448536829734695346889 + 142蒙上阴影。
def primes(n, skip_range=None):
"""Modified "primes" with the original assertion from P.S. of the question.
with skipping of an unimportant huge range.
>>> primes(71)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
>>> # The smallest failing number for the first failing prime 71:
>>> big_n = 10986448536829734695346889
>>> primes(big_n + 2 * 71, (72, big_n))
Traceback (most recent call last):
AssertionError
"""
if not skip_range:
primes = list(range(2, n + 1))
else:
primes = list(range(2, skip_range[0]))
primes.extend(range(skip_range[1], n + 1))
p = 0
while p < len(primes):
c = p + 1
while c < len(primes):
if(primes[c] % primes[p] == 0):
primes.pop(c)
if c < len(primes):
assert primes[c] % primes[p] != 0
c = c + 1
p = p + 1
return primes
# Verify that it can fail.
aprime = 71 # the first problematic prime
FIRST_BAD_NUMBERS = (
10986448536829734695346889, 11078434793489708690791399,
12367063025234804812185529, 20329913969650068499781719,
30697401499184410328653969, 35961932865481861481238649,
40008133490686471804514089, 41414505712084173826517629,
49440212368558553144898949, 52201441345368693378576229)
for bad_number in FIRST_BAD_NUMBERS:
try:
primes(bad_number + 2 * aprime, (aprime + 1, bad_number))
raise Exception('The number {} should fail'.format(bad_number))
except AssertionError:
print('{} OK. It fails as is expected'.format(bad_number))
我通过搜索n个模数小素数的可能余数,通过像拼图这样的复杂算法解决了这些数字。最后一个简单的步骤是获得完整的n(通过三行Python代码中的中文余数定理)。我知道所有120个小于primorial(71)= 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71
的基本解决方案会定期重复此数字的所有倍数。我为每十年测试过的素数多次重写了算法,因为每十年解决方案的速度比以前慢很多。也许我在可接受的时间内找到一个较小的解决方案,使用相同的算法73或79。
编辑:
我想找到一个完整的无声失败的不安全的原始函数。也许存在一些由不同素数组成的候选人。这种解决方案只能推迟最终结果。时间和资源的每一步都会变得越来越昂贵。因此,只有由一个或两个素数组成的数字才具有吸引力。
我希望隐藏的候选人 c 只有两个解决方案是好的:c = p ** n
或c = p1 * p ** n
或c = p1 ** n1 * p ** n
其中 p 和 p1 是素数, n 是一个大于1的幂。 primes 函数失败,如果c - 2 * p
可被整除,则小于0 p 如果 c-2n 和 c 之间的所有数字都可以被小于 p 的任何素数整除。变量p1 * p ** n还要求 p1 ( p1&lt; p )之前相同的 c 失败,正如我们已经知道的那样无数的这样的候选人。
编辑:我发现一个较小的示例失败:编号为121093190175715194562061为素数79.(比71少了约九十倍)我可以&#39;继续使用相同的算法来查找较小的示例,因为我的笔记本电脑上的所有702612基本解决方案花费了超过30小时的时间。
我还对所有小于400000000(4E10)的候选人以及所有相关的素数进行了验证,没有候选人会在问题中失败。直到你有数TB的内存和数千年的时间,算法中的断言才会通过,因为你的时间复杂度是O((n / log(n))^ 2)或非常相似。
答案 1 :(得分:4)
你的观察似乎是准确的,这是一个很好的捕获。
我怀疑它起作用的原因,至少在某些情况下,是因为复合数字实际上被分解为多个素数。因此,内部循环可能会错过第一个因子的值,但随后它会在后来的因子中获取它。
对于一个小的“n”,您可以打印出列表的值,看看是否正在发生这种情况。
顺便提一下,这种寻找素数的方法是基于Eratothenes的筛选。在筛选时,如果“c”是“p”的倍数,那么下一个值永远不会是同一个素数的倍数。
问题是:在任何情况下,p * x和p *(x + 1)之间的所有值都可以被一些小于p和p * x + 1的素数整除。 (这是算法会遗漏一个值的地方,以后不会被捕获。)但是,其中一个值是偶数,所以它将在圆“2”上消除。因此,真正的问题是,是否存在p * x和p *(x + 2)之间的所有值都可以被小于p的数字整除的情况。
副手,我想不到满足这个条件的任何小于100的数字。对于p = 5,总有一个值在两个连续的5的倍数之间不能被2或3整除。
似乎有很多关于素数空位和序列的文章,但不是那么多关于连续整数的序列,可以被小于p的数字整除。经过一些(好的,很多)试验和错误之后,我已经确定39,474(17 * 2,322)和39,491(17 * 2,233)之间的每个数字都可以被小于17的整数整除:
39,475 5
39,476 2
39,477 3
39,478 2
39,479 11
39,480 2
39,481 13
39,482 2
39,483 3
39,484 2
39,485 5
39,486 2
39,487 7
39,488 2
39,489 3
39,490 2
我不确定这是否是第一个这样的值。但是,我们必须找到两倍于此的序列。我认为这不太可能,但不确定是否有证据。
我的结论是原始代码可能有效,但您的修复是正确的。没有证据证明没有这样的序列,它看起来像一个bug,尽管这个bug可能是非常非常非常罕见的。
答案 2 :(得分:1)
给定q(下一个更高的除数)&gt; p,那么如果n可被q整除,那么可被q整除的下一个数是n + q> n + p>米 所以m应该在当前迭代中跳过以进行可分性测试
Here n = primes[c]
m = primes[c + 1], i.e. primes[c] after primes.pop(c)
p = primes[p]
q = primes[p+1]
答案 3 :(得分:0)
这是一个想法:
Triptych解释 1 c之后的下一个数字不能是c + p,但我们仍然需要证明它也永远不会是c + 2p。
如果我们使用primes = [2],我们只能有一个连续的“非素数”,一个可被2整除的数字。
如果我们使用primes = [2,3]我们可以构造3个连续的“非素数”,数字除以2,数字除以3,数字除以2,它们不能得到下一个数字。或
2,3,4 =&gt;连续3次“非素数”
尽管2和3不是“非素数”,但我更容易根据这些数字进行思考。
如果我们使用[2,3,5],我们会得到
2,3,4,5,6 =&gt; 5连续“非素数”
如果我们使用[2,3,5,7],我们会得到
2,3,4,5,6,7,8,9,10 =&gt; 9连续“非素数”
出现了这种模式。我们可以得到的最连续的非素数是下一个素数 - 2。
因此,如果next_prime&lt; p * 2 + 1,我们必须在c和c + 2p之间至少有一些数字,因为在给定素数的情况下,连续非素数的数量不够长。
我不知道非常大的数字,但我认为next_prime < p * 2 + 1
可能会有非常大的数字。
我希望这是有道理的,并增加一些亮点。
1 Triptych的答案已被删除。
答案 4 :(得分:0)
这并没有提供一个远程结论性答案,但这是我在此尝试的内容:
我在这里重申了所需的假设(lpf代表最低素数因子):
For any composite number, x, where:
lpf(x) = n
There exists a value, m, where 0 < m < 2n and:
lpf(x+m) > n
可以很容易地证明x的值存在于不存在复合数(x + m)以满足不等式的情况。任何平方素数表明:
lpf(x) = x^.5, so x = n^2
n^2 + 2n < (n + 1)^2 = n^2 + 2n + 1
因此,在任何平方素数的情况下,为了使其成立,必须有一个素数p,在x 我认为可以得出结论,正方形的渐近分布(x ^ .5)与素数定理(素数的渐近分布约为x /(ln x))相比,但是,实际上,我的理解素数定理充其量是有限的。 我没有任何策略可以将这个结论扩展到非方形复合数字,所以这可能不是一个有用的途径。 我使用上面重述的问题整理了一个程序测试值。 直接测试此语句应该只删除运行所述算法的任何幸运结果。通过获得幸运的结果,我指的是被跳过的值可能不安全,但是由于跳过的值不能被当前正在迭代的数字整除,或者被删除,因此不会出现任何不正确的结果通过后续迭代获得。本质上,如果算法得到正确的结果,但要么没有找到每个消除值的最小素数因子,要么没有严格检查每个素数结果,我对它不满意。如果存在这样的情况,我认为有理由认为案件也存在不幸运的地方(尽管它们可能不常见),并且会产生不正确的结果。 然而,运行我的测试显示在2到2,000,000的值中没有反例。因此,对于它的价值,除非我的逻辑不正确,否则所述算法的值应至少是2,000,000,除非我的逻辑不正确。 这就是我必须添加的内容。很棒的问题,Phazyck,玩得很开心!
答案 5 :(得分:-2)
如果素数p除了候选者c,则可被p整除的下一个较大候选者是c + p。因此,您的原始代码是正确的。
然而,制作素数列表是一种腐烂的方式;尝试使用n = 1000000,看看它有多慢。问题是,当您使用筛子时,您正在进行试验分割。这是一个简单的筛子(伪代码,我会让你翻译成Python或其他语言):
function primes(n)
sieve := makeArray(2..n, True)
for p from 2 to n step 1
if sieve[p]
output p
for i from p+p to n step p
sieve[i] := False
这应该会在不到一秒的时间内获得不到一百万的素数。还有其他筛选算法甚至更快。
这种算法被称为Eratosthenes的筛子,大约在2200年前由希腊数学家发明。 Eratosthenes是一个有趣的家伙:除了筛选素数之外,他还发明了闰日和纬度和经度系统,准确地计算了从太阳到地球和地球周长的距离,并且暂时是托勒密图书馆的首席图书馆馆长。在亚历山大。
当您准备好了解有关素数编程的更多信息时,我在我的博客上谦虚地推荐这个essay。