C #, 10039 28164 digit
6 0{28157} 169669
Sunting: Saya membuat program lain berdasarkan algoritma Qualtagh dengan beberapa modifikasi kecil:
- Saya mencari angka dari bentuk L000 ... 000R, di mana L adalah komposit kiri, R adalah komposit kanan. Saya membiarkan angka komposit L kiri memiliki beberapa digit, meskipun ini sebagian besar merupakan perubahan gaya, dan mungkin tidak mempengaruhi efisiensi algoritma.
- Saya telah menambahkan multithreading untuk mempercepat pencarian.
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;
}
}
Jawaban lama:
8 0{5436} 4 0{4600} 1
Berikut adalah beberapa pola penting untuk bilangan prima rapuh:
600..00X00..009
900..00X00..009
800..00X00..001
999..99X99..999
di mana X bisa 1, 2, 4, 5, 7 atau 8.
Untuk angka seperti itu kita hanya perlu mempertimbangkan (panjang - 1) mungkin Remove
operasi yang . Remove
Operasi lain menghasilkan duplikat atau angka gabungan. Saya mencoba mencari semua angka dengan 800 digit dan memperhatikan bahwa 4 pola muncul lebih sering daripada yang lain: 8007001, 8004001, 9997999, dan 6004009. Karena Emil dan Jakube menggunakan pola 999X999, saya memutuskan untuk menggunakan 8004001 saja. untuk menambah variasi.
Saya telah menambahkan optimasi berikut ke algoritme:
- Saya mulai mencari dari angka dengan 7000 digit dan kemudian menambah panjang 1500 setiap kali prime rapuh ditemukan. Jika tidak ada prime rapuh dengan panjang yang diberikan maka saya menambahnya dengan 1. 7000 dan 1500 hanya angka acak yang sepertinya cocok.
- Saya menggunakan multithreading untuk mencari angka dengan panjang berbeda pada waktu yang bersamaan.
- Hasil dari setiap pemeriksaan utama disimpan dalam tabel hash untuk mencegah pemeriksaan duplikat.
- Saya menggunakan implementasi Miller-Rabin dari Mpir.NET , yang sangat cepat (MPIR adalah fork dari 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);
}
}