Compact by-divisor candidate filtering
This commit is contained in:
1 parent
4ab70ec4a8
commit
adac9ff5f9
3 files changed
+115
-25
No files matched your search
@@ -322,7 +322,7 @@ public class MersenneDivisorCycles
|
|||||||
factorCounts[2UL] = twoCount;
|
factorCounts[2UL] = twoCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AccumulateFactors(exponent, factorCache, factorCounts) || !AccumulateFactors(k, factorCache, factorCounts))
|
if (!AccumulateFactors(exponent, factorCache, factorCounts, cacheResult: true) || !AccumulateFactors(k, factorCache, factorCounts, cacheResult: false))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -334,14 +334,15 @@ public class MersenneDivisorCycles
|
|||||||
private static bool AccumulateFactors(
|
private static bool AccumulateFactors(
|
||||||
ulong value,
|
ulong value,
|
||||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||||
Dictionary<ulong, int> counts)
|
Dictionary<ulong, int> counts,
|
||||||
|
bool cacheResult)
|
||||||
{
|
{
|
||||||
if (value <= 1UL)
|
if (value <= 1UL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetFactorization(value, cache, out FactorCacheEntry factorization))
|
if (!TryGetFactorization(value, cache, cacheResult, out FactorCacheEntry factorization))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -361,6 +362,7 @@ public class MersenneDivisorCycles
|
|||||||
private static bool TryGetFactorization(
|
private static bool TryGetFactorization(
|
||||||
ulong value,
|
ulong value,
|
||||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||||
|
bool cacheResult,
|
||||||
out FactorCacheEntry factorization)
|
out FactorCacheEntry factorization)
|
||||||
{
|
{
|
||||||
if (cache is not null && cache.TryGetValue(value, out factorization))
|
if (cache is not null && cache.TryGetValue(value, out factorization))
|
||||||
@@ -389,7 +391,7 @@ public class MersenneDivisorCycles
|
|||||||
Array.Sort(primes, exponents);
|
Array.Sort(primes, exponents);
|
||||||
factorization = new FactorCacheEntry(primes, exponents, count);
|
factorization = new FactorCacheEntry(primes, exponents, count);
|
||||||
|
|
||||||
if (cache is not null)
|
if (cache is not null && cacheResult)
|
||||||
{
|
{
|
||||||
cache[value] = factorization;
|
cache[value] = factorization;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
@@ -27,29 +28,97 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
_ = threadCount;
|
_ = threadCount;
|
||||||
|
|
||||||
|
candidates.Sort();
|
||||||
|
Span<ulong> candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
||||||
|
int candidateCount = candidateSpan.Length;
|
||||||
|
|
||||||
bool applyStartPrime = startPrime > 0UL;
|
bool applyStartPrime = startPrime > 0UL;
|
||||||
int skippedByPreviousResults = 0;
|
int skippedByPreviousResults = 0;
|
||||||
List<ulong> primesToTest = new(candidates.Count);
|
|
||||||
|
if (previousResults is not null && previousResults.Count > 0)
|
||||||
|
{
|
||||||
|
int recordedCount = previousResults.Count;
|
||||||
|
ulong[] recordedCandidates = ArrayPool<ulong>.Shared.Rent(recordedCount);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Span<ulong> recordedSpan = recordedCandidates.AsSpan(0, recordedCount);
|
||||||
|
int recordedIndex = 0;
|
||||||
|
foreach (ulong value in previousResults.Keys)
|
||||||
|
{
|
||||||
|
recordedSpan[recordedIndex++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordedSpan.Sort();
|
||||||
|
|
||||||
|
int writeIndex = 0;
|
||||||
|
int skipIndex = 0;
|
||||||
|
|
||||||
|
for (int readIndex = 0; readIndex < candidateCount; readIndex++)
|
||||||
|
{
|
||||||
|
ulong candidate = candidateSpan[readIndex];
|
||||||
|
|
||||||
|
while (skipIndex < recordedCount && recordedSpan[skipIndex] < candidate)
|
||||||
|
{
|
||||||
|
skipIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipIndex < recordedCount && recordedSpan[skipIndex] == candidate)
|
||||||
|
{
|
||||||
|
skippedByPreviousResults++;
|
||||||
|
skipIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidateSpan[writeIndex++] = candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writeIndex < candidateCount)
|
||||||
|
{
|
||||||
|
candidates.RemoveRange(writeIndex, candidateCount - writeIndex);
|
||||||
|
candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
||||||
|
candidateCount = candidateSpan.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<ulong>.Shared.Return(recordedCandidates, clearArray: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skippedByPreviousResults > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Skipped {skippedByPreviousResults.ToString(CultureInfo.InvariantCulture)} candidates excluded by previous results.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
if (applyStartPrime)
|
||||||
|
{
|
||||||
|
startIndex = candidates.BinarySearch(startPrime);
|
||||||
|
if (startIndex < 0)
|
||||||
|
{
|
||||||
|
startIndex = ~startIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (startIndex > 0 && candidates[startIndex - 1] >= startPrime)
|
||||||
|
{
|
||||||
|
startIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ulong> primesToTest = candidates;
|
||||||
|
Span<ulong> primesSpan = CollectionsMarshal.AsSpan(primesToTest);
|
||||||
ulong maxPrime = 0UL;
|
ulong maxPrime = 0UL;
|
||||||
|
int primeWriteIndex = 0;
|
||||||
|
|
||||||
using IEnumerator<ulong> primeEnumerator = Prime.Numbers.GetEnumerator();
|
using IEnumerator<ulong> primeEnumerator = Prime.Numbers.GetEnumerator();
|
||||||
bool hasPrime = primeEnumerator.MoveNext();
|
bool hasPrime = primeEnumerator.MoveNext();
|
||||||
ulong currentPrime = hasPrime ? primeEnumerator.Current : 0UL;
|
ulong currentPrime = hasPrime ? primeEnumerator.Current : 0UL;
|
||||||
|
|
||||||
for (int index = 0; index < candidates.Count; index++)
|
for (int index = startIndex; index < candidateCount; index++)
|
||||||
{
|
{
|
||||||
ulong candidate = candidates[index];
|
ulong candidate = primesSpan[index];
|
||||||
|
|
||||||
if (applyStartPrime && candidate < startPrime)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousResults is not null && previousResults.ContainsKey(candidate))
|
|
||||||
{
|
|
||||||
skippedByPreviousResults++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (candidate <= 1UL)
|
if (candidate <= 1UL)
|
||||||
{
|
{
|
||||||
@@ -76,17 +145,17 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
primesSpan[primeWriteIndex++] = candidate;
|
||||||
|
|
||||||
if (candidate > maxPrime)
|
if (candidate > maxPrime)
|
||||||
{
|
{
|
||||||
maxPrime = candidate;
|
maxPrime = candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
primesToTest.Add(candidate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skippedByPreviousResults > 0)
|
if (primeWriteIndex < primesToTest.Count)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Skipped {skippedByPreviousResults.ToString(CultureInfo.InvariantCulture)} candidates excluded by previous results.");
|
primesToTest.RemoveRange(primeWriteIndex, primesToTest.Count - primeWriteIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primesToTest.Count == 0)
|
if (primesToTest.Count == 0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -50,8 +50,27 @@ public readonly struct MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong
|
|||||||
|
|
||||||
internal static class MontgomeryDivisorDataCache
|
internal static class MontgomeryDivisorDataCache
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<ulong, MontgomeryDivisorData> Cache = new();
|
[ThreadStatic]
|
||||||
|
private static bool s_hasCachedValue;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ulong s_cachedModulus;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MontgomeryDivisorData s_cachedData;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static MontgomeryDivisorData Get(ulong modulus) => Cache.GetOrAdd(modulus, static m => MontgomeryDivisorData.FromModulus(m));
|
public static MontgomeryDivisorData Get(ulong modulus)
|
||||||
|
{
|
||||||
|
if (s_hasCachedValue && s_cachedModulus == modulus)
|
||||||
|
{
|
||||||
|
return s_cachedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
MontgomeryDivisorData computed = MontgomeryDivisorData.FromModulus(modulus);
|
||||||
|
s_cachedModulus = modulus;
|
||||||
|
s_cachedData = computed;
|
||||||
|
s_hasCachedValue = true;
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user