寻找最大的脆弱素数


21

考虑一下从位置处的数字开始从数字Remove(n, startIndex, count)中删除count数字的功能。例子:nstartIndex

Remove(1234, 1, 1) = 234
Remove(123456, 2, 3) = 156
Remove(1507, 1, 2) = 07 = 7
Remove(1234, 1, 4) = 0

如果所有可能的Remove操作将其设为非素数,我们将称素数X易碎。例如,80651是易碎的素数,因为以下所有数字都不是素数:

651, 51, 1, 0, 8651, 851, 81, 8, 8051, 801, 80, 8061, 806, 8065

目标

编写一个找到最大的易碎素数的程序。编辑:删除了时间限制,因为有一种相对公平的方法可以规避它。

分数是程序发现的易碎素数。如果出现平局,则较早的提交者获胜。

规则

  • 您可以使用任何语言和任何第三方库。
  • 您可以在自己的硬件上运行该程序。
  • 您可以使用概率素数测试。
  • 一切都在基数10中。

领先的作品

  • Qualtagh(Java)6629位
  • Emil(Python 2)的5048位数字
  • Jakube(Python 2)的2268位数字

编辑:我已经添加了自己的答案。

  • Suboptimus Prime的28164位数字,基于Qualtagh的算法(C#)

5
即使我不对答案进行硬编码,我也可以从非常脆弱的素数开始进行搜索。显然,没有人希望从1开始搜索。是什么阻止我这样做呢?在被要求对答案进行基本的硬编码之前,我到底可以开始搜索多远?我喜欢挑战。
2014年

2
@SuboptimusPrime您可以改为完全删除时间限制,因为我相信在某个时候这将非常罕见,无论如何找到下一个将是一项壮举。(类似codegolf.stackexchange.com/questions/41021/...
马丁安德


7
对于计算机速度较慢的人,您仍然处于不利地位
John Dvorak

11
我花了一个令人尴尬的长时间,才意识到“编写一个找到最大易碎素数的程序”并不意味着“存在最大易碎素数。编写一个找到它的程序。” 我想我做了太多的欧拉计划。:-P
鲁阿克

Answers:


9

Java- 3144 3322 6629位数字

6 0{3314} 8969999

6 0{6623} 49099

该解决方案基于FryAmTheEggman的答案

  1. 最后一位是1或9。
  2. 如果最后一位是1,则前一位是0、8或9。
  3. 如果最后一位是9,则前一位是0、4、6或9。
  4. ...

如果我们深入研究该怎么办?

它变成一个树形结构:

                        S
             -----------------------
             1                     9
    ------------------         ----------------
    0           8    9         0    4    6    9
---------     -----
0   8   9      ...

如果R及其所有结尾都是复合的,我们称数字R为右复合。

我们将以广度优先的方式遍历所有正确的复合数字:1、9、01、81、91、09、49、69、99、001、801、901等。

从零开始的数字不会检查素数,但需要它来构建其他数字。

我们将以X00 ... 00R的形式查找目标数N,其中X是4、6、8或9之一,R是正确的复合数。X不能为素数。X不能为0。X不能为1,因为如果R以1或9结尾,则N将包含11或19。

如果XR在“删除”操作后包含质数,则XYR对于任何Y也将包含质数。因此,我们不应遍历从R开始的分支。

令X为常数,例如6。

伪代码:

X = 6;
for ( String R : breadth-first-traverse-of-all-right-composites ) {
  if ( R ends with 1 or 9 ) {
    if ( remove( X + R, i, j ) is composite for all i and j ) {
      for ( String zeros = ""; zeros.length() < LIMIT; zeros += "0" ) {
        if ( X + zeros + R is prime ) {
          // At this step these conditions hold:
          // 1. X + 0...0 is composite.
          // 2. 0...0 + R = R is composite.
          // 3. X + 0...0 + R is composite if 0...0 is shorter than zeros.
          suits = true;
          for ( E : all R endings )
            if ( X + zeros + E is prime )
              suits = false;
          if ( suits )
            print R + " is fragile prime";
          break; // try another R
                 // because ( X + zeros + 0...0 + R )
                 // would contain prime ( X + zeros + R ).
        }
      }
    }
  }
}

我们应该限制零的数量,因为找到形式为X +零+ R的素数可能会花费太长时间(如果它们都是复合的,则可能永远都是这样)。

真正的代码很冗长,可以在这里找到。

通过确定性的Miller检验变体对长整数范围内的数字进行素数检验。对于BigInteger编号,首先进行试验划分,然后进行BailliePSW测试。这是概率,但可以肯定。而且它比Miller-Rabin测试更快(我们应该对Miller-Rabin中的这么大的数字进行多次迭代,以获取足够的精度)。

编辑:第一次尝试是不正确的。如果X0 ... 0R是素数,我们也应该忽略以R开头的分支。那么X0 ... 0YR不会是脆弱的素数。因此,添加了额外的检查。此解决方案似乎是正确的。

编辑2:添加了优化。如果(X + R)可被3整除,那么(X +零+ R)也可被3整除。因此(X +零+ R)在这种情况下不能为素数,并且可以跳过此类R。

编辑3:如果素数不在最后一位或第一位,则不必跳过素数。所以像21或51这样的结尾就可以了。但这并没有太大改变。

结论:

  1. 我的最后一个答案是检查100分钟是否脆弱。搜索答案(检查所有先前的变体)大约花费了15分钟。是的,限制搜索时间没有任何意义(我们可以从目标编号开始搜索,因此时间将为零)。但是像这个问题一样,限制检查时间可能是有意义的。
  2. 答案60 ... 049099中间有数字4。如果触摸“删除”操作,该数字将被3整除。因此,我们应检查左侧和右侧的删除操作。右侧太短。左侧长度几乎为n = length(N)。
  3. 诸如BPSW和Miller-Rabin之类的素数测试使用恒定数量的模幂。根据此页面,其复杂度为O(M(n)* n),其中M(n)是乘法复杂度。Java使用Toom-Cook和Karatsuba算法,但是为了简单起见,我们将采用Scholar算法。M(n)= n 2。因此,素数测试的复杂度为O(n 3)。
  4. 我们应该检查所有从length = 6到6629的数字。让我们以min = 1和max = n为通用。整个检查复杂度为O(1 3 + 2 3 + ... + n 3)= O((n *(n + 1)/ 2)2)= O(n 4)。
  5. Emil的答案具有相同的检查渐近性。但是常数因子较低。数字“ 7”位于数字的中间。左侧和右侧可以几乎相等。得出(n / 2)4 * 2 = n 4 /8。加速:8倍。9 ... 9Y9 ... 9格式的数字可以比具有相同检查时间的X0 ... 0R格式的数字长1.7倍。

1
感谢您的功劳,但是您的算法比我的算法复杂得多!伟大的工作,欢迎来到PPCG!:)
FryAmTheEggman 2014年

@FryAmTheEggman:谢谢你的想法!令人鼓舞。
Qualtagh 2014年

您对检查复杂性的分析非常有趣,但是搜索兼容性可能也很重要。我认为您的算法所需的大数素数测试(与Emil相比)要少得多,才能找到大的脆弱素数。而且,您可以使用本机库来加快素数测试的速度。我正在使用Mpir.NET,只需数分钟即可检查您的号码是否是易碎的素数。
Suboptimus Prime

13

Python的2 - 126 1221 1337 1719 2268位

999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

'9' * 1944 + '7' + '9' * 323

Remove(n,startIndex,count)的结果数约为len(n)^ 2个。我试图尽量减少这些数字。如果彼此相邻的许多数字相同,则这些结果数字中的许多数字会被忽略,因为它们会出现多次。

所以我把它推到了极致,中间只有9s和一点质数。我还研究了100万以下的脆弱素数,发现其中存在如此脆弱的素数。搜索末尾带有2 9的数字确实很好,不确定原因。最后的1个数字,3个或4个9s会导致较小的易碎素数。

它使用pyprimes模块。我不确定是否有好处。它使用miller_rabin测试,因此具有概率性。

程序在大约1分钟内找到了这个126位易碎素数,其余时间搜索失败。

biggest_found = 80651

n = lambda a,b,c: '9'*a + b + '9'*c

for j in range(1000):
   for digit in '124578':
      for i in range(2000):
         number = int(n(i,digit,j))
         if is_prime(number):
            if (number > biggest_found):
               if all(not is_prime(int(n(i,digit,k))) for k in range(j)):
                  biggest_found = number
                  print(i+j+1, biggest_found)
            break

编辑:

刚刚看到,您删除了时间限制。我将在晚上运行程序,也许会出现一些非常大的易碎素数。

编辑2:

使我的原始程序更快,所以仍然没有超过126位的解决方案。所以我跳上火车,搜索x 9s +1位数字+ y 9s。好处是,如果固定y,则必须检查O(n)个数是否为素。它很快找到了1221。

编辑3:

对于2268位数字,我使用同一程序,仅将工作划分为多个内核。


3
“大约1分钟内”-很抱歉,必须报告一个多元化的“错误”。:P
hichris123

我的最后几篇文章困扰着米勒·拉宾的概率性质。您可能还需要使用其他算法进行验证。
2014年

为什么只检查从末尾去除数字形成的数字是复合数字?为什么不检查从前面去除数字形成的数字?
isaacg 2014年

1
因为我之前在“ for i”循环中检查了这些内容。在这里,我在开头添加9s,并进行质数检查。当我找到此格式的第一个质数时,我知道所有开头都小于9的数字都不是质数。在检查完最后删除9s之后,我停止(中断),因为现在每个数字都有一个质数,因此不是质数。
雅库布2014年

啊,非常聪明。
isaacg 2014年

7

Python 2.7-429623069 99993799

到目前为止,没有任何优化。仅使用一些关于易碎素数的琐碎观察(感谢聊天中的Rainbolt):

  1. 易碎的素数必须以1或9结尾(素数不是偶数,并且最后一位不能为素数)
  2. 以1结尾的易碎素数必须以8或9开头(第一个数字不能为素数,而11、41和61均为素数)
  3. 以9结尾的易碎素数必须以4,6或9开头(请参阅1的推理,但只有89是素数)

只是试图使球滚动:)

从技术上讲,这会稍微超过15分钟,但只会在额外的时间里检查一个数字。

is_prime是从此处(isaacg 在此处使用)获取的,并且是概率性的。

def substrings(a):
    l=len(a)
    out=set()
    for i in range(l):
        for j in range(l-i):
            out.add(a[:i]+a[len(a)-j:])
    return out

import time

n=9
while time.clock()<15*60:
    if is_prime(n):
        if not any(map(lambda n: n!='' and is_prime(int(n)), substrings(`n`))):
            print n
    t=`n`
    if n%10==9 and t[0]=='8':n+=2
    elif n%10==1 and t[0]!='8':n+=8
    elif t[0]=='1' or is_prime(int(t[0])):n+=10**~-len(t)
    else:n+=10

刚一说明,当我开始用这个n=429623069我起床482704669。多余的数字确实似乎扼杀了这一策略。


一开始还不错!尽管is_prime似乎对32位值执行了完整的确定性检查,但这有点多余。我认为is_prime方法可能会更快,如果您要注释掉整个试用版部分。
Suboptimus Prime

@SuboptimusPrime哦,谢谢。我什至都没看过它:P
FryAmTheEggman 2014年

@SuboptimusPrime我认为对于小值而言,完全确定性检查会更快,因为作者定义了在候选因素之间采取的步骤。再次感谢您的想法,但它留下的:)时似乎要快得多
FryAmTheEggman

小修正你的答案:91 = 13X7,所以91是复合材料,并在1结束脆弱的素数可与9.真正开始
Suboptimus总理

@SuboptimusPrime完全正确,不知道我是怎么弄糟的。我发布的值仍然有效,因为我只是跳过了一些可能的值。
FryAmTheEggman 2014年

7

Python 2,828位 5048位

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
155*'9'+'7'+4892*'9'

正如@Jakube所指出的,由于代码中的错误,我提交的第一个素数实际上并不脆弱。修复该错误很容易,但同时也使算法明显变慢。

我将自己限制在脆弱素数的易于搜索的子集,即仅包含数字9和恰好一个数字7的子集。

def fragile_prime_generator(x, b_max):
  bs, cs = set(), set()
  prime = dict()

  def test_prime(b,c):
    if (b,c) not in prime:
      prime[(b,c)] = is_prime(int('9'*b+`x`+'9'*c))
    return prime[(b,c)]

  def test_frag(b,c):
    for b2 in xrange(b):
      if test_prime(b2,c):
        bs.add(b2)
        return False
    for c2 in xrange(c):
      if test_prime(b,c2):
        cs.add(c2)
        return False
    return True

  a = 1
  while len(bs)<b_max:
    for b in xrange(min(a, b_max)):
      c = a-b
      if b not in bs and c not in cs and test_prime(b,c):
        bs.add(b)
        cs.add(c)
        if test_frag(b,c): yield b,c
    a += 1
  print "no more fragile primes of this form"

for b,c in fragile_prime_generator(7, 222):
  print ("%d digit fragile prime found: %d*'9'+'%d'+%d*'9'"
          % (b+c+1, b, x, c))

我使用了与@FryAmTheEggman 相同的is_prime功能(从此处开始)。

编辑:

我进行了两项更改以使算法更快:

  • 我尝试跳过尽可能多的素数检查,并且仅在发现潜在的易碎素数以确保它确实易碎时才返回。重复检查很少,因此我粗略地记住了主要检查功能。

  • 对于表格的数字,b*'9' + '7' + c*'9'我限制了的大小b。限制越低,必须检查的数字就越少,但根本找不到任何大的易碎素数的机会就会增加。我随意选择222作为限制。

一次素数校验已经达到几千位数,可能会使我的程序花费几秒钟。因此,我可能无法用这种方法做得更好。

请随时检查我提交的内容的正确性。由于概率素数检查,理论上我的数字可能不是素数,但如果是,它应该是脆弱的。或者我做错了什么。:-)


2
您发现的素数并不脆弱。如果调用Remove(n,83,838)[删除除前82位数字以外的所有内容],您将得到一个质数。
雅库布

1
啊,谢谢@Jakube。我试图变得太聪明。原来我跳过了应有的素数检查。我正在修复它。
艾米尔(Emil)2014年

1
再次检查,现在您的结果是正确的。
雅库布

根据我的程序,您的5048位数字确实是一个易碎的素数。
Suboptimus Prime

@SuboptimusPrime:很好,谢谢您的检查!
艾米尔(Emil)2014年

4

C#,10039 28164位

6 0{28157} 169669

编辑:我已经基于Qualtagh的算法做了另一个程序,做了一些小的修改:

  • 我正在搜索L000 ... 000R形式的数字,其中L是左组合,R是右组合。我允许左复合数字L包含多个数字,尽管这主要是一种风格上的变化,并且可能不会影响算法的效率。
  • 我添加了多线程来加快搜索速度。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const int PrimeNotFound = int.MaxValue;

    private static BitArray _primeSieve;
    private static HashSet<Tuple<int, int>> _templatesToSkip = new HashSet<Tuple<int, int>>();

    static void Main(string[] args)
    {
        int bestDigitCount = 0;
        foreach (Tuple<int, int> template in GetTemplates())
        {
            int left = template.Item1;
            int right = template.Item2;
            if (SkipTemplate(left, right))
                continue;

            int zeroCount = GetZeroCountOfPrime(left, right);
            if (zeroCount != PrimeNotFound)
            {
                int digitCount = left.ToString().Length + right.ToString().Length + zeroCount;
                if (digitCount >= bestDigitCount)
                {
                    string primeStr = left + " 0{" + zeroCount + "} " + right;
                    Console.WriteLine("testing " + primeStr);
                    bool isFragile = IsFragile(left, right, zeroCount);
                    Console.WriteLine(primeStr + " is fragile: " + isFragile);

                    if (isFragile)
                        bestDigitCount = digitCount;
                }

                _templatesToSkip.Add(template);
            }
        }
    }

    private static int GetZeroCountOfPrime(int left, int right)
    {
        _zeroCount = 0;

        int threadCount = Environment.ProcessorCount;
        Task<int>[] tasks = new Task<int>[threadCount];
        for (int i = 0; i < threadCount; i++)
            tasks[i] = Task.Run(() => InternalGetZeroCountOfPrime(left, right));
        Task.WaitAll(tasks);

        return tasks.Min(task => task.Result);
    }

    private static int _zeroCount;

    private static int InternalGetZeroCountOfPrime(int left, int right)
    {
        const int maxZeroCount = 40000;
        int zeroCount = Interlocked.Increment(ref _zeroCount);
        while (zeroCount <= maxZeroCount)
        {
            if (zeroCount % 1000 == 0)
                Console.WriteLine("testing " + left + " 0{" + zeroCount + "} " + right);

            if (IsPrime(left, right, zeroCount))
            {
                Interlocked.Add(ref _zeroCount, maxZeroCount);
                return zeroCount;
            }
            else
                zeroCount = Interlocked.Increment(ref _zeroCount);
        }

        return PrimeNotFound;
    }

    private static bool SkipTemplate(int left, int right)
    {
        for (int leftDiv = 1; leftDiv <= left; leftDiv *= 10)
            for (int rightDiv = 1; rightDiv <= right; rightDiv *= 10)
                if (_templatesToSkip.Contains(Tuple.Create(left / leftDiv, right % (rightDiv * 10))))
                    return true;

        return false;
    }

    private static bool IsPrime(int left, int right, int zeroCount)
    {
        return IsPrime(left.ToString() + new string('0', zeroCount) + right.ToString());
    }

    private static bool IsPrime(string left, string right, int zeroCount)
    {
        return IsPrime(left + new string('0', zeroCount) + right);
    }

    private static bool IsPrime(string s)
    {
        using (mpz_t n = new mpz_t(s))
        {
            return n.IsProbablyPrimeRabinMiller(20);
        }
    }

    private static bool IsFragile(int left, int right, int zeroCount)
    {
        string leftStr = left.ToString();
        string rightStr = right.ToString();

        for (int startIndex = 0; startIndex < leftStr.Length - 1; startIndex++)
            for (int count = 1; count < leftStr.Length - startIndex; count++)
                if (IsPrime(leftStr.Remove(startIndex, count), rightStr, zeroCount))
                    return false;

        for (int startIndex = 1; startIndex < rightStr.Length; startIndex++)
            for (int count = 1; count <= rightStr.Length - startIndex; count++)
                if (IsPrime(leftStr, rightStr.Remove(startIndex, count), zeroCount))
                    return false;

        return true;
    }

    private static IEnumerable<Tuple<int, int>> GetTemplates()
    {
        const int maxDigitCount = 8;
        PreparePrimeSieve((int)BigInteger.Pow(10, maxDigitCount));
        for (int digitCount = 2; digitCount <= maxDigitCount; digitCount++)
        {
            for (int leftCount = 1; leftCount < digitCount; leftCount++)
            {
                int rightCount = digitCount - leftCount;
                int maxLeft = (int)BigInteger.Pow(10, leftCount);
                int maxRight = (int)BigInteger.Pow(10, rightCount);

                for (int left = maxLeft / 10; left < maxLeft; left++)
                    for (int right = maxRight / 10; right < maxRight; right++)
                        if (IsValidTemplate(left, right, leftCount, rightCount))
                            yield return Tuple.Create(left, right);
            }

        }
    }

    private static void PreparePrimeSieve(int limit)
    {
        _primeSieve = new BitArray(limit + 1, true);
        _primeSieve[0] = false;
        _primeSieve[1] = false;

        for (int i = 2; i * i <= limit; i++)
            if (_primeSieve[i])
                for (int j = i * i; j <= limit; j += i)
                    _primeSieve[j] = false;
    }

    private static bool IsValidTemplate(int left, int right, int leftCount, int rightCount)
    {
        int rightDigit = right % 10;
        if ((rightDigit != 1) && (rightDigit != 9))
            return false;

        if (left % 10 == 0)
            return false;

        if ((left + right) % 3 == 0)
            return false;

        if (!Coprime(left, right))
            return false;

        int leftDiv = 1;
        for (int i = 0; i <= leftCount; i++)
        {
            int rightDiv = 1;
            for (int j = 0; j <= rightCount; j++)
            {
                int combination = left / leftDiv * rightDiv + right % rightDiv;
                if (_primeSieve[combination])
                    return false;

                rightDiv *= 10;
            }

            leftDiv *= 10;
        }

        return true;
    }

    private static bool Coprime(int a, int b)
    {
        while (b != 0)
        {
            int t = b;
            b = a % b;
            a = t;
        }
        return a == 1;
    }
}

旧答案:

8 0{5436} 4 0{4600} 1

这是一些易碎素数的著名模式:

600..00X00..009
900..00X00..009
800..00X00..001
999..99X99..999

其中X可以是1、2、4、5、7或8。

对于此类数字,我们仅需考虑(长度-1)种可能的Remove操作。另一个Remove运算会产生重复数或明显是合成数。我尝试搜索最多800个数字的所有此类数字,并发现出现了4种模式,而其余频率则更高:8007001、8004001、9997999和6004009。由于Emil和Jakube使用的是999X999模式,因此我决定只使用8004001添加一些变化。

我在算法中添加了以下优化:

  • 我从具有7000位数字的数字开始搜索,然后每次发现易碎的质数时将长度增加1500。如果没有给定长度的易碎素数,则将其递增1。7000和1500只是看起来合适的任意数字。
  • 我正在使用多线程来同时搜索具有不同长度的数字。
  • 每个主要检查的结果都存储在哈希表中,以防止重复检查。
  • 我正在使用Mpir.NET的 Miller-Rabin实现,该实现非常快(MPIR是GMP的分支)。
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const string _template = "8041";

    private static ConcurrentDictionary<Tuple<int, int>, byte> _compositeNumbers = new ConcurrentDictionary<Tuple<int, int>, byte>();
    private static ConcurrentDictionary<int, int> _leftPrimes = new ConcurrentDictionary<int, int>();
    private static ConcurrentDictionary<int, int> _rightPrimes = new ConcurrentDictionary<int, int>();

    static void Main(string[] args)
    {
        int threadCount = Environment.ProcessorCount;
        Task[] tasks = new Task[threadCount];
        for (int i = 0; i < threadCount; i++)
        {
            int index = i;
            tasks[index] = Task.Run(() => SearchFragilePrimes());
        }
        Task.WaitAll(tasks);
    }

    private const int _lengthIncrement = 1500;
    private static int _length = 7000;
    private static object _lengthLock = new object();
    private static object _consoleLock = new object();

    private static void SearchFragilePrimes()
    {
        int length;
        lock (_lengthLock)
        {
            _length++;
            length = _length;
        }

        while (true)
        {
            lock (_consoleLock)
            {
                Console.WriteLine("{0:T}: length = {1}", DateTime.Now, length);
            }

            bool found = false;
            for (int rightCount = 1; rightCount <= length - 2; rightCount++)
            {
                int leftCount = length - rightCount - 1;
                if (IsFragilePrime(leftCount, rightCount))
                {
                    lock (_consoleLock)
                    {
                        Console.WriteLine("{0:T}: {1} {2}{{{3}}} {4} {2}{{{5}}} {6}",
                            DateTime.Now, _template[0], _template[1], leftCount - 1,
                            _template[2], rightCount - 1, _template[3]);
                    }
                    found = true;
                    break;
                }
            }

            lock (_lengthLock)
            {
                if (found && (_length < length + _lengthIncrement / 2))
                    _length += _lengthIncrement;
                else
                    _length++;
                length = _length;
            }
        }
    }

    private static bool IsFragilePrime(int leftCount, int rightCount)
    {
        int count;
        if (_leftPrimes.TryGetValue(leftCount, out count))
            if (count < rightCount)
                return false;

        if (_rightPrimes.TryGetValue(rightCount, out count))
            if (count < leftCount)
                return false;

        if (!IsPrime(leftCount, rightCount))
            return false;

        for (int i = 0; i < leftCount; i++)
            if (IsPrime(i, rightCount))
                return false;

        for (int i = 0; i < rightCount; i++)
            if (IsPrime(leftCount, i))
                return false;

        return true;
    }

    private static bool IsPrime(int leftCount, int rightCount)
    {
        Tuple<int, int> tuple = Tuple.Create(leftCount, rightCount);
        if (_compositeNumbers.ContainsKey(tuple))
            return false;

        using (mpz_t n = new mpz_t(BuildStr(leftCount, rightCount)))
        {
            bool result = n.IsProbablyPrimeRabinMiller(20);

            if (result)
            {
                _leftPrimes.TryAdd(leftCount, rightCount);
                _rightPrimes.TryAdd(rightCount, leftCount);
            }
            else
                _compositeNumbers.TryAdd(tuple, 0);

            return result;
        }
    }

    private static string BuildStr(int leftCount, int rightCount)
    {
        char[] chars = new char[leftCount + rightCount + 1];
        for (int i = 0; i < chars.Length; i++)
            chars[i] = _template[1];
        chars[0] = _template[0];
        chars[leftCount + rightCount] = _template[3];
        chars[leftCount] = _template[2];
        return new string(chars);
    }
}

当我尝试验证您的第一个答案时,您已经发布了一个新答案)。检查已经花费了24小时。答案似乎是正确的。我不敢相信Java的BigInteger比本地实现要慢得多。我想慢了2、3甚至10倍。但是24小时相对于几分钟来说太多了。
Qualtagh 2014年

@Qualtagh公平地说,由于算法较差,找到10039位数字花了35个小时:)我当前的程序大约需要3分钟才能找到您的6629位数字,而花6小时才能找到28164位数字。
Suboptimus Prime

您的第一个答案是正确的。已验证!验证耗时48小时。而且我什至不会尝试验证第二个答案))。我想知道为什么BigInteger与MPIR相比这么慢。只是JVM /本机差异吗?我设置了一个“ -server”标志,因此希望代码是JIT编译的。模幂运算的算法不同:Java和MPIR都使用2 <sup> k </ sup>元滑动窗口,但是Java中k = 3是固定的,MPIR根据指数的大小选择k。MPIR是在多个内核上使用并行计算还是在GPU功能上使用?Java的BigInteger没有。
Qualtagh 2014年

1
@Qualtagh我很确定MPIR仅使用一个CPU内核。我自己添加了多线程,这使四核CPU的搜索速度提高了近4倍。我没有比较MPIR和Java BigInteger的内部实现,但是我想MPIR使用更好的算法进行乘法和模除。而且,它可能更好地针对64位CPU进行了优化(请参阅此博客文章中的基准)。
Suboptimus Prime

2
MPIR确实是单核,并且不使用GPU。它是C和汇编代码的高度优化和微调的混合。有一个MPIR版本仅使用C(出于可移植性原因),但是C + ASM版本明显更快。我用于MPIR.Net的MPIR版本是使用K8(第一代x64)指令集的C + ASM,因为我希望MPIR.Net可以在所有x64 PC上运行。在我的加密基准测试中,后面的指令集的版本没有明显更快,但是对于其他操作,这当然可能有所不同。
约翰·雷诺兹

2

Haskell- 1220 1277个数字 为真实的固定

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997999999999999999999999999999999999999999999999999999999999999999999999

9{1150} 7 9{69}

更好的1277位数字

9{871} 8 9{405}

Haskell代码

downADigit :: Integer -> [Integer]
downADigit n = f [] 1 where
     f xs a | nma /= n = f (((n `div` a10)*a + nma):xs) a10
            | otherwise = xs where
        a10 = a * 10
        nma = n `mod` a

isFragile = all (not . isPrime') . downADigit
findNextPrime :: Integer -> Integer
findNextPrime n | even n = f (n + 1)
                | otherwise = f n where
    f n | isPrime' n  = n
        | otherwise = f (n + 2)

primesFrom n = f (findNextPrime n) where
    f n = n:f (findNextPrime $ n + 1)

primeLimit = 10000

isPrime' n | n < primeLimit = isPrime n
isPrime' n = all (millerRabinPrimality n) [2,3,5,7,11,13,17,19,984,7283,6628,8398,2983,9849,2739]

-- (eq. to) find2km (2^k * n) = (k,n)
find2km :: Integer -> (Integer,Integer)
find2km n = f 0 n
    where 
        f k m
            | r == 1 = (k,m)
            | otherwise = f (k+1) q
            where (q,r) = quotRem m 2        

-- n is the number to test; a is the (presumably randomly chosen) witness
millerRabinPrimality :: Integer -> Integer -> Bool
millerRabinPrimality n a
    | a <= 1 || a >= n-1 = 
        error $ "millerRabinPrimality: a out of range (" 
              ++ show a ++ " for "++ show n ++ ")" 
    | n < 2 = False
    | even n = False
    | b0 == 1 || b0 == n' = True
    | otherwise = iter (tail b)
    where
        n' = n-1
        (k,m) = find2km n'
        b0 = powMod n a m
        b = take (fromIntegral k) $ iterate (squareMod n) b0
        iter [] = False
        iter (x:xs)
            | x == 1 = False
            | x == n' = True
            | otherwise = iter xs

-- (eq. to) pow' (*) (^2) n k = n^k
pow' :: (Num a, Integral b) => (a->a->a) -> (a->a) -> a -> b -> a
pow' _ _ _ 0 = 1
pow' mul sq x' n' = f x' n' 1
    where 
        f x n y
            | n == 1 = x `mul` y
            | r == 0 = f x2 q y
            | otherwise = f x2 q (x `mul` y)
            where
                (q,r) = quotRem n 2
                x2 = sq x

mulMod :: Integral a => a -> a -> a -> a
mulMod a b c = (b * c) `mod` a
squareMod :: Integral a => a -> a -> a
squareMod a b = (b * b) `rem` a

-- (eq. to) powMod m n k = n^k `mod` m
powMod :: Integral a => a -> a -> a -> a
powMod m = pow' (mulMod m) (squareMod m)

-- simple for small primes
primes :: [Integer]
primes = 2:3:primes' where
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n)
                                   $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0
isPrime :: Integer -> Bool
isPrime n | n < 2 = False
          | otherwise = f primes where
            f (p:ps) | p*p <= n = if n `rem` p == 0 then False else f ps
                     | otherwise = True

main = do
    print . head $ filter isFragile (primesFrom $ 10^1000)

我认为您可以删除除最后3个以外的所有内容...
Sp3000 2014年

如果我删除了最后3个,则它以5结尾,因此可以被5整除
John Meacham

2
不,我的意思是删除所有内容,直到剩下最后3个,这是素数。
Sp3000

1
@JohnMeacham我的程序建议,如果从左侧删除386位数字,则此数字变为质数。
Suboptimus Prime

1
请在发布之前验证您的电话号码。如果从1276位数字中删除左边的1256位数字,则会得到99999994999999999999,这是质数。
Suboptimus Prime
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.