最大の壊れやすい素数を見つける


21

positionの数字から始まる数字から数字Remove(n, startIndex, count)を削除する関数を考えます。例:countnstartIndex

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による6629桁(Java)
  • Emilによる5048桁(Python 2)
  • ジャクベによる2268桁(Python 2)

編集:私は自分の答えを追加しました。

  • Qualtaghのアルゴリズム(C#)に基づいたSuboptimus Primeによる28164桁

5
答えをハードコーディングしなくても、大きな壊れやすい素数に非常に近いポイントで検索を開始できます。明らかに、1で検索を開始したい人は誰もいません。基本的に答えをハードコーディングするために呼び出される前に、どのくらい正確に検索を開始できますか?ちなみに私は挑戦が大好きです。
レインボルト14年

2
@SuboptimusPrime代わりに時間制限を完全に削除することもできます。ある時点で、とにかく次のものを見つけるのは偉業になると信じているからです。(codegolf.stackexchange.com/questions/41021/…に類似)
マーティン・エンダー


7
あなたはまだ不利に遅いコンピュータを持っている人を残している
ジョンドヴォルザーク

11
「最大の脆弱な素数を見つけるプログラムを作成する」とは、「最大の脆弱な素数が存在する。それを検出するプログラムを作成する」という意味ではないことを理解するのに、私は非常に長い時間がかかりました。私はあまりにも多くのプロジェクトオイラーをやったと思います。:-P
ruakh

回答:


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にすることはできません。また、Rが1または9で終わる場合、Nには11または19が含まれるため、Xを1にすることはできません。

「削除」操作後に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 + zeros + Rの形式の素数を見つけるのに時間がかかりすぎる可能性があるため、ゼロの量を制限する必要があります(すべてが複合の場合は永遠に)。

実際のコードは非常に冗長で、ここにあります

長整数範囲の数値の素数性テストは、Millerテストの決定論的なバリアントによって実行されます。BigInteger番号の場合、最初に試行除算が実行され、次にBailliePSWテストが実行されます。確率的ですが、かなり確実です。そして、Miller-Rabinテストよりも高速です(十分な精度を得るには、Miller-Rabinでこのような大きな数値に対して多くの反復を行う必要があります)。

編集:最初の試みは間違っていました。X0 ... 0Rが素数の場合、Rで始まる分岐も無視する必要があります。X0 ... 0YRは壊れやすい素数ではありません。そのため、追加のチェックが追加されました。この解決策は正しいようです。

編集2:最適化を追加しました。(X + R)が3で割り切れる場合、(X + zeros + R)も3で割り切れるので、この場合(X + zeros + 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アルゴリズムを使用しますが、簡単にするためにスカラーアルゴリズムを使用します。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スピードアップ:8X。9 ... 9Y9 ... 9の形式の数値は、同じチェック時間を持つX0 ... 0Rの形式よりも1.7倍長くなる可能性があります。

1
クレジットをありがとう、しかしあなたのアルゴリズムは私のものよりもはるかに複雑です!お疲れ様でした。PPCGへようこそ!:)
FryAmTheEggman 14年

@FryAmTheEggman:アイデアをありがとう!刺激的です。
クアルタグ14年

チェックの複雑さの分析は非常に興味深いですが、検索の複雑さもおそらく重要です。あなたのアルゴリズムは、大きな壊れやすい素数を見つけるために、(Emilに比べて)多数の素数テストが著しく少ないと思います。また、ネイティブライブラリを使用して、素数性テストを高速化できます。私はMpir.NETを使用していますが、壊れやすい素数であるかどうかを確認するには数分かかります。
準最適プライム14年

13

パイソン2 - 126 1221 1337 1719 2268桁

999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

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

Remove(n、startIndex、count)の約len(n)^ 2個の結果の数があります。私はそれらの数を最小化しようとしました。隣り合う数字が同じ場合、これらの数字の多くは無視される可能性があります。複数回表示されるためです。

だから私はそれを極端なものにしました。たったの9秒で、中央に少し素数があります。また、100万未満の脆弱な素数を調べたところ、このような脆弱な素数があることがわかりました。最後に2 9の数字を検索するのは本当にうまくいきますが、理由はわかりません。末尾に1つの数字、3、または4つの9があると、壊れやすい素数が小さくなります。

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 14年

ミラーラビンの確率的性質は、私の最後のいくつかのエントリのために私をかみました。別のアルゴリズムで検証することもできます。
ジョンミーチャム

末尾から数字を削除して形成された数字が合成されていることだけをチェックするのはなぜですか?前面から数字を削除して形成された数字を確認してみませんか?
isaacg 14年

1
前に「for i」ループでこれらをチェックしたためです。ここで、先頭に9を追加し、プライムチェックを行います。この形式の最初の素数を見つけたとき、最初の9より小さい数字はすべて素数ではないことを知っています。そして、最後に9を削除することを確認した後、停止(中断)します。これは、すべての数字に素数が含まれているため、素数ではないためです。
ジャクベ14年

ああ、とても賢い。
isaacg 14年

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分をわずかに超えて実行されますが、余分な時間に1つの数値のみをチェックします。

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メソッドの方が速くなると思います。
準最適プライム14年

@SuboptimusPrimeああ、ありがとう。私もそれを見ていませんでした:P
FryAmTheEggman 14年

@SuboptimusPrime完全な決定論的チェックは、小さな値の場合、著者が候補因子の間に取り入れる手順を定義したため、高速だと思います。アイデアに再び感謝しますが、それを離れるときはずっと速いようです:)
FryAmTheEggman 14年

あなたの答えに小さな補正:91 = 13x7は、とても91は複合体である、と1で終わる脆弱な素数は実際には9で起動することができます
Suboptimus首相

@SuboptimusPrimeまったくその通りです。投稿した値はまだ有効なはずです。可能な値をいくつかスキップしたからです。
FryAmTheEggman 14年

7

Python 2、828桁 5048桁

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

@Jakubeが指摘したように、私が提出した最初の素数は、コードのバグのために実際には脆弱ではありませんでした。バグの修正は簡単でしたが、アルゴリズムが大幅に遅くなりました。

私は、脆弱な素数の簡単に検索可能なサブセット、つまり数字9と正確に1つの数字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関数(ここから)を使用しました。

編集:

アルゴリズムを高速化するために2つの変更を加えました。

  • できるだけ多くの素数チェックをスキップし、壊れやすい素数が見つかった場合にのみ戻って、本当に壊れやすいことを確認しようとします。重複チェックは少数なので、プライムチェック機能を大まかにメモしました。

  • フォームの数についてはb*'9' + '7' + c*'9'、のサイズを制限しましたb。制限が低いほど、チェックする必要のある数字は少なくなりますが、大きな壊れやすい素数がまったく見つからない可能性が高くなります。制限として222を任意に選択しました。

数千桁で、1つの素数チェックですでにプログラムに数秒かかることがあります。そのため、このアプローチではおそらくこれ以上のことはできません。

私の提出物の正確さをチェックしてください。確率的素数チェックのため、私の数は理論的には素数ではないかもしれませんが、もしそうなら、それは壊れやすいはずです。または、何か間違ったことをしました。:-)


2
見つかった素数は壊れません。Remove(n、83,838)[最初の82桁を除くすべてを削除]を呼び出すと、素数になります。
ジャクベ14年

1
ああ、@ジャクベに感謝。私はあまりにも賢くしようとしていた。私が持っているべきであるより多くの素数チェックをスキップしていたことが判明しました。私はそれを修正しようとしています。
エミル14年

1
もう一度確認しました。結果は正しいです。
ジャクベ14年

私のプログラムによれば、5048桁の数字は確かに壊れやすい素数です。
準最適プライム14年

@SuboptimusPrime:チェックしてくれてありがとう!
エミル14年

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桁までの数字を検索しようとすると、8007001、8004001、99797、6004009の4つのパターンが頻繁に現れることに気付きました。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

@Qualtagh公平を期すと、10039桁の数字はアルゴリズムが劣るため35時間かかりました。)現在のプログラムは6629桁の数字を見つけるのに約3分かかり、28164桁の数字を見つけるのに6時間かかります。
準最適プライム14年

最初の答えは正しいです。確認済み!検証には48時間かかりました。そして、2番目の答えを検証しようとさえしません))。BigIntegerがMPIRと比較して非常に遅いのはなぜかと思っています。JVM /ネイティブの違いだけですか?「-server」フラグを設定したため、コードがJITコンパイルされることを期待しています。モジュラーべき乗のアルゴリズムは異なります。JavaとMPIRの両方が2 <sup> k </ sup>のスライディングウィンドウを使用しますが、k = 3はJavaで固定され、MPIRは指数のサイズに応じてkを選択します。MPIRはいくつかのコアで並列計算を使用していますか、それともGPU機能ですか?JavaのBigIntegerはそうではありません。
クアルタグ14年

1
@Qualtagh MPIRが使用しているCPUコアは1つだけだと確信しています。自分でマルチスレッドを追加した結果、クアッドコアCPUでの検索がほぼ4倍高速になりました。MPIRとJava BigIntegerの内部実装を比較しませんでしたが、MPIRは乗算とモジュラー除算により良いアルゴリズムを使用していると思います。また、おそらく64ビットCPU向けに最適化されています(このブログ投稿のベンチマークをご覧ください)。
準最適プライム14年

2
MPIRは確かにシングルコアであり、GPUを使用しません。これは、Cコードとアセンブラコードの高度に最適化および微調整された組み合わせです。C(移植性の理由)のみを使用するMPIRバージョンがありますが、C + ASMバージョンは著しく高速です。MPIR.Netに使用しているMPIRバージョンは、K8(第1世代x64)命令セットを使用したC + ASMです。MPIR.Netをすべてのx64 PCで実行したかったためです。私の暗号ベンチマークでは、後の命令セットのバージョンはそれほど高速ではありませんでしたが、もちろん他の操作では異なる場合があります。
ジョンレイノルズ14年

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 14年

最後の3つを削除すると5で終了するため、5で割り切れます
ジョン

2
いいえ、最後の3つだけが残るまですべてを削除するということです。
Sp3000 14年

1
@JohnMeacham私のプログラムは、左から386桁を削除すると、この数が素数になることを示唆しています。
準最適プライム14年

1
投稿する前に番号を確認してください。1276桁の数字から左の1256桁を削除すると、99999994999999999999が得られます。これは素数です。
準最適プライム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.