Знайдіть найбільшу тендітну групу


21

Розглянемо функцію, Remove(n, startIndex, count)яка вилучає countцифри з числа, nпочинаючи з цифри в позиції startIndex. Приклади:

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

Ми будемо називати просте число X тендітним, якщо кожна можлива Removeоперація робить його непросте. Наприклад, 80651 є крихким простим рівнем, оскільки всі наведені нижче числа не є простими:

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

Мета

Напишіть програму, яка знайде найбільшу тендітну групу. Редагувати: вилучено обмеження часу, оскільки існує досить справедливий спосіб його обійти.

Оцінка - це тендітне просте число, знайдене вашою програмою. У разі вирівнювання матчу виграє більш рання подача.

Правила

  • Ви можете використовувати будь-яку мову та будь-які сторонні бібліотеки.
  • Ви запускаєте програму на власному обладнанні.
  • Ви можете використовувати ймовірнісні тести на первинність.
  • Все в базі 10.

Провідні записи

  • 6629 цифр від Qualtagh (Java)
  • 5048 цифр Еміля (Python 2)
  • 2268 цифр Якубе (Python 2)

Редагувати: я додав власну відповідь.

  • 28164 цифри Субоптимуса Прайма, засновані на алгоритмі Qualtagh (C #)

5
Навіть якщо я не зашифрую відповідь, я можу почати пошук у дуже близькому до крихкого простору. Очевидно, ніхто не хоче починати пошук з 1. Що заважає мені це робити? Наскільки точно я можу розпочати пошук, перш ніж мене зателефонують за принципово жорстким кодуванням відповіді? Я люблю виклик до речі.
Rainbolt

2
@SuboptimusPrime Натомість ви могли б взагалі зняти обмеження часу, тому що я вважаю, що в якийсь момент це буде настільки рідко, що в будь-якому разі буде подвигом знайти наступного. (Подібно до codegolf.stackexchange.com/questions/41021/… )
Мартін Ендер


7
Ви все ще залишаєтесь у недолі тих, хто має повільніші комп’ютери
Джон Дворак

11
Мені потрібно було бентежно тривалий час зрозуміти, що "Написати програму, яка знаходить найбільшу тендітну групу", це не означало "Існує найбільший крихкий прем'єр. Напишіть програму, яка її знаходить". Я думаю, що я зробив занадто багато проекту Euler. :-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 і т.д.

Числа, що починаються з нуля, не перевіряються на первинність, але потрібні для побудови подальших чисел.

Ми будемо шукати цільове число N у вигляді X00 ... 00R, де 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 (або назавжди, якщо всі вони складені).

Справжній код досить багатослівний і його можна знайти тут .

Тестування первинності чисел у великому діапазоні int проводиться детермінованим варіантом тесту Міллера. Для номерів BigInteger спочатку проводиться пробний поділ, а потім тест BailliePSW. Це ймовірнісне, але цілком певне. І це швидше, ніж тест Міллера-Рабіна (нам слід зробити багато ітерацій для таких великих чисел у Міллера-Рабіна, щоб отримати достатню точність).

Редагувати: перша спроба була неправильною. Ми також повинні ігнорувати гілки, починаючи з R, якщо X0 ... 0R є простим. Тоді X0 ... 0YR не був би крихким. Тож була додана додаткова перевірка. Це рішення здається правильним.

Редагування 2: додана оптимізація. Якщо (X + R) ділиться на 3, то (X + нулі + R) також ділиться на 3. Тож (X + нулі + R) в цьому випадку не може бути простим, і такі R можуть бути пропущені.

Редагування 3: пропускати проміжні цифри не потрібно, якщо вони не знаходяться на останній чи першій позиції. Тож закінчення як 21 або 51 - це нормально. Але це нічого не змінює.

Висновки:

  1. Моя остання відповідь була перевірка на те, що вона неміцна протягом 100 хвилин. Пошук відповіді (перевірка всіх попередніх варіантів) зайняв близько 15 хвилин. Так, немає сенсу обмежувати час пошуку (ми можемо почати пошук з цільового числа, тому час буде нульовим). Але може бути доцільним обмежити час перевірки, як у цьому питанні .
  2. Відповідь 60 ... 049099 має цифру 4 посередині. Якщо операція "видалити" торкнеться її, число стає дільним на 3. Тому слід перевірити операції з видалення в лівій і правій частині. Права сторона занадто коротка. Довжина лівої сторони майже n = довжина (N).
  3. Тести на первинність, такі як BPSW та Міллер-Рабін, використовують постійну кількість модульних експоненцій. Його складність становить O (M (n) * n) відповідно до цієї сторінки , де M (n) - складність множення. Java використовує алгоритми Toom-Cook і Karatsuba, але ми будемо використовувати алгоритм науковця для простоти. M (n) = n 2 . Отже, складність тестування первинності становить O (n 3 ).
  4. Ми повинні перевірити всі числа від довжини = 6 до 6629. Візьмемо min = 1 і max = n для спільності. Вся складність перевірки становить O (1 3 + 2 3 + ... + n 3 ) = O ((n * (n + 1) / 2) 2 ) = O (n 4 ).
  5. Відповідь Еміля має ті ж асимптотики перевірки. Але постійний коефіцієнт нижчий. Цифра "7" стоїть посередині числа. Лівий і правий бік можуть бути майже рівними. Він дає (n / 2) 4 * 2 = n 4 / 8. Швидкість: 8X. Числа у формі 9 ... 9Y9 ... 9 можуть бути в 1,7 рази довші, ніж у формі X0 ... 0R, що мають однаковий час перевірки.

1
Дякую за кредит, але ваш алгоритм набагато складніший за мій! Чудова робота, і ласкаво просимо до PPCG! :)
FryAmTheEggman

@FryAmTheEggman: дякую за ідею! Це надихає.
Qualtagh

Ваш аналіз складності перевірки дуже цікавий, але, мабуть, важливість пошуку також є важливою. Я думаю, що ваш алгоритм вимагає значно менших тестів на первинність великих чисел (порівняно з Емілем), щоб знайти великий крихкий простір. А ви можете пришвидшити тести первинності за допомогою рідної бібліотеки. Я використовую Mpir.NET, і перевірка вашого номера на те, що він є неміцним, просто займає кілька хвилин.
Субоптимус Прем'єр

13

Пітон 2 - 126 1221 1337 1719 2268 цифр

999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

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

Є приблизно приблизно len (n) ^ 2 результуючі числа Remove (n, startIndex, count). Я намагався мінімізувати ці цифри. Якщо поруч одна цифра є однаковою, то багато цих результатів можна ігнорувати, оскільки вони відображаються багаторазово.

Тому я взяв це до крайності, всього 9-х та трохи розквіту в середині. Я також подивився на тендітну групу менше 1 мільйона і побачив, що є такі крихкі розквіту. Пошук номерів з 2 9 в кінці працює дуже добре, не знаю, чому. 1 число, 3 або 4 9 в кінцевому підсумку призводить до менших крихких прайменів.

Він використовує модуль pyprimes . Я не впевнений, якщо це добре. Він використовує тест miller_rabin, тому він є ймовірним.

Програма знайде цю 126-значну тендітну лінійку приблизно за 1 хвилину, а решту часу вона шукає без успіху.

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. Перевага полягає в тому, що вам потрібно перевірити номери O (n) на первинність, якщо ви виправите y. Він знаходить 1221 досить швидко.

редагувати 3:

Для 2268-значного числа я використовую ту саму програму, тільки розділив роботу на кілька ядер.


3
"приблизно за 1 хвилину" - вибачте, доведеться повідомити про помилку про плюралізацію. : P
hichris123

Імовірнісний характер мельника-рабіна - це те, що кусало мене за останні кілька моїх записів. Ви можете також перевірити за допомогою іншого алгоритму.
Джон Мехам

Чому ви лише перевіряєте, що числа, утворені від вилучення цифр з кінця, є складовими? Чому б не перевірити цифри, утворені, видаляючи цифри спереду?
isaacg

1
Тому що я попередньо перевіряв їх у циклі 'for i'. Тут я додаю 9 на початку і роблю основну перевірку. Коли я знаходжу перше просте число цієї форми, я знаю, що всі числа з меншим числом 9 на початку не є простими. І після перевірки на вилучення 9 в кінці я зупиняюсь (перериваю), бо зараз кожне число має просте число в ньому і тому не є простим.
Якубе

Ах, дуже розумний.
isaacg

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 може працювати швидше, якщо ви прокоментуєте повну частину пробного поділу.
Субоптимус Прем'єр

@SuboptimusPrime О, дякую. Я навіть не дивився на це: P
FryAmTheEggman

@SuboptimusPrime Я думаю, що повна детермінована перевірка є швидшою для малих значень, оскільки автор визначив кроки, які слід зробити між кандидатами-факторами. Ще раз дякую за ідею, але здається набагато швидше, залишаючи це в :)
FryAmTheEggman

Невелика корекція вашої відповіді: 91 = 13x7, тож 91 є складовою, і крихкі праймери, що закінчуються на 1, насправді можуть починатися з 9.
Suboptimus Prime

@SuboptimusPrime Цілком правильно, не знаю, як я зіпсував це. Значення, яке я розмістив, все одно має бути дійсним, оскільки я просто пропускав деякі можливі значення.
FryAmTheEggman

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))

Я використовував ту саму is_primeфункцію ( звідси ), що і @FryAmTheEggman.

Редагувати:

Я вніс дві зміни, щоб зробити алгоритм швидшим:

  • Я намагаюся пропустити якомога більше перевірок первинності і повертаюсь лише тоді, коли знайдеться потенційний тендітний праймер, щоб переконатися, що він справді крихкий. Існує невелика кількість дублікатів чеків, тому я жорстоко запам'ятав функцію просте перевірки.

  • Для номерів форми b*'9' + '7' + c*'9'я обмежив розмір b. Чим нижча межа, тим менше число потрібно перевіряти, але шанси збільшуються, щоб взагалі не знайти великого крихкого простору. Я наче свавільно вибрав 222 як межу.

При кількох тисячах цифр один простий перевірка вже може зайняти мою програму кілька секунд. Тож я, мабуть, не можу значно покращити цей підхід.

Будь ласка, не соромтеся перевірити правильність мого подання. Через імовірнісну перевірку первинності моє число теоретично не може бути простим, але якщо воно є, воно повинно бути неміцним. Або я зробив щось не так. :-)


2
Знайдений праймер не тендітний. Якщо ви зателефонуєте на Видалити (n, 83,838) [Видалити все, окрім перших 82 цифр], у вас закінчиться праймером.
Якубе

1
Ах, дякую @Jakube Я намагався бути занадто розумним. Виявляється, я пропускав більше перевірок первинності, тоді я повинен був. Я на шляху виправити це.
Еміль

1
Перевірив це ще раз, тепер ваші результати правильні.
Якубе

Ваш номер із 5048 цифрами, справді, є крихким основним моєю програмою.
Субоптимус Прем'єр

@SuboptimusPrime: Чудово, дякую за перевірку!
Еміль

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. Оскільки Еміль і Якубе використовують шаблон 999X999, я вирішив використовувати 8004001 просто додати трохи різноманітності.

До алгоритму я додав такі оптимізації:

  • Я починаю шукати цифри з 7000 цифрами, а потім збільшувати довжину на 1500 щоразу, коли виявляється крихка простичка. Якщо немає крихкої простір із заданою довжиною, то я збільшую її на 1. 7000 і 1500 - це лише довільні числа, які здавались відповідними.
  • Я використовую багатопотокове запитання для пошуку чисел з різною довжиною одночасно.
  • Результат кожної першої перевірки зберігається в хеш-таблиці, щоб запобігти повторенню перевірок.
  • Я використовую реалізацію Miller-Rabin від Mpir.NET , що дуже швидко (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 годин на пошук через неповноцінний алгоритм :) Моїй теперішній програмі потрібно близько 3 хвилин, щоб знайти ваш цифровий номер 6629, і 6 годин, щоб знайти номер 28164.
Субоптимус Прем'єр

Ваша перша відповідь правильна. Перевірено! Перевірка зайняла 48 годин. І я навіть не спробую перевірити другу відповідь)). Мені цікаво, чому BigInteger настільки повільний у порівнянні з MPIR. Це лише JVM / рідна різниця? Я встановлюю прапор "-server", тому очікую, що код буде зібраний JIT. Алгоритми модульної експоненції різняться: і Java, і MPIR використовують 2 <sup> k </sup> -розсувне вікно, але k = 3 фіксується в Java і MPIR вибирає k відповідно до розміру показника. Чи використовує MPIR паралельні обчислення на декількох ядрах чи, можливо, GPU? Java BigInteger не робить.
Qualtagh

1
@Qualtagh Я впевнений, що MPIR використовує лише одне ядро ​​CPU. Я додав багатопотоковість, що призвело до майже в 4 рази швидшого пошуку на чотирьохядерному процесорі. Я не порівнював внутрішню реалізацію MPIR та Java BigInteger, але, мабуть, MPIR використовує кращі алгоритми для множення та модульного поділу. Крім того, це, мабуть, краще оптимізовано для 64-бітних процесорів (див. Орієнтир у цій публікації блогу ).
Субоптимус Прем'єр

2
MPIR - це дійсно одноядерний і не використовує GPU. Це вкрай оптимізований та тонко налаштований поєднання C та асемблерського коду. Існує версія MPIR, яка використовує лише C (з міркувань портативності), але версія C + ASM помітно швидша. Версія MPIR, яку я використовую для MPIR.Net, - це C + ASM, використовуючи набір інструкцій K8 (1-й рід x64), тому що я хотів, щоб MPIR.Net запускався на всіх комп'ютерах x64. Версії для пізніших наборів інструкцій не були помітно швидшими в моєму криптовалюті, але це, звичайно, може відрізнятися для інших операцій.
Джон Рейнольдс

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

він закінчується через 5, якщо я видалю останні 3, тож ділиться на 5
Джон Мехем

2
Ні, я маю на увазі видалити все, поки у вас залишилися лише останні 3, що є простим.
Sp3000

1
@JohnMeacham Моя програма передбачає, що це число стає простим, якщо вилучите 386 цифр зліва.
Субоптимус Прем'єр

1
Будь ласка, підтвердьте свої номери перед публікацією. Якщо ви вилучите ліві 1256 цифр зі свого 1276-значний номер, ви отримаєте 9999999499999999999999, що є простим.
Субоптимус Прем'єр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.