Project Euler #3 STUCK
本文关键字:STUCK Euler Project | 更新日期: 2023-09-27 18:00:25
我真的不希望事情发展到这样,当然体育的重点是我应该自己解决问题。
前两个问题我在不到一个小时的时间里解决了,不幸的是,我完全陷入了问题3。现在我尝试了传统的逐试除法,当然这太慢了,我试图通过用sqrt(n)进行逐试除法优化,但仍然太慢,无法获得600851475143的最高素数。
现在,我甚至尝试使用埃拉托斯梯尼筛来实现这一点,我不得不承认,我几乎不理解,但它仍然没有解决这个问题,首先,"标记"数字的数量会导致抛出异常,因为当你试图生成高达600851475143的所有素数时,条目的数量会变得巨大。
我被卡住了,坦率地说,我现在开始对此感到恼火。。
这是我的代码:
class Program
{
// The prime factors of 13195 are 5, 7, 13 and 29.
// What is the largest prime factor of the number 600851475143 ?
static void Main(string[] args)
{
Console.WriteLine(GetHighestPrimeFactor(600851475143).ToString());
Console.ReadKey();
}
static List<long> GeneratePrimeNumbers(long n)
{
// Let's use the Sieve of Eratosthenes for this
var markedNumbers = new List<long>();
var primes = new List<long>();
for (long i = n / 2; i < n; i++)
{
// If this is false then this is a prime number, add it to the list of primes
if (!markedNumbers.Contains(i))
{
primes.Add(i);
for (long j = i; j <= n; j+= i)
{
markedNumbers.Add(j);
}
}
}
return primes;
}
static long GetHighestPrimeFactor(long n)
{
var primes = GeneratePrimeNumbers(n);
// Loop backwards and return when we hit the first prime number
for (long i = n / 2; i > 1; i--)
{
if (n % i == 0)
{
if (primes.Contains(i))
{
return i;
}
}
}
//Code should not reach this point of execution
return -1;
}
体育课的重点当然是我应该努力自己解决问题。
是的,这里有一些提示而不是答案。
我试着通过用sqrt(n)进行除法来优化除法
我看不出这有什么帮助。假设你正试图找到最大的素数74。74的平方根是8点多。74除以8有什么帮助?
现在我甚至试着用埃拉托色尼筛来做
筛子是用来寻找素数的。你的理论是,你要找到所有素数,直到所需的数,然后找到除数的最大素数吗?
对于一个大的数字来说,这在计算上太昂贵了。
以下是您的提示:
-
如果一个数n>0是素数,那么它就是它自己最大的素数。
-
如果一个数字n>0是复合的,那么它是x*y,其中两者都不是1。
-
这两个数字中的一个小于或等于n的平方根。
-
n的最大素数等于x的最大素数或y的最大素数。
如果你不明白为什么其中一个事实是真的,停下来好好想想,直到你明白为止。除非你冷静下来,否则在接下来的几个欧拉问题上你永远不会有任何进展。
现在有了这些提示,您应该能够将问题分解为一系列较小的问题。
我自己也读过关于埃拉托斯特尼筛的文章,但我对素数分解的技术了解不多。我不确定从生成所有已知素数的列表开始是否是一种很好的技术。并不是说这很糟糕,我只是怀疑也许更实用的技术不采用这种方法,因为它会有巨大的空间需求。
除此之外,我将为您的素数列表的内存使用问题提出一些修复建议。
首先,我会将GeneratePrimeNumbers的结果写入分段文件,而不是将它们添加到内存列表中,每个文件都覆盖一些素数块(相当大,但在容易加载到内存的范围内)。也许第一行是关于文件中最大/最小素数的一些信息。
在GeneratePrimeNumbers中,从包含最大素数的页面开始(只将该页面从文件加载到列表中,选择一个有O(1)时间的C#数据类型。包含,我认为哈希集会很好),当你向后工作并交叉到一个新的段时,删除当前素数页面的列表并加载新的素数页面。这将解决您的内存使用问题。如果您确保每个页面都包含相同数量的素数(或者自上一页以来的最大素数可能是部分的),那么您可以重用现有列表,或者使用Capacity==将列表初始化为页面大小。无论使用什么数据结构,它在构造函数中都可能有一个容量选项,您希望传递这个选项,因为它将确保它提前请求所需的内存量。如果你有300个素数,并且没有设置capcity,那么当你添加到列表时,列表的内部数组大小会翻倍。例如,当它跨越256时,它会在内部创建一个新的512项数组,并将256项复制到新的512大小数组。这意味着当跨越这样的边界时,列表使用的内存量是原来的三倍,因为它同时具有旧数组和新数组,新数组是原来数组的2倍。复制完成后,就不需要旧的数组了。重点是,如果你不预先设置容量,那么当你只需要大约三分之一的可用内存时,你可能会出现内存不足的异常。
我可能会让GeneratePrimeNumbers只检查预先存在的文件,如果传入的数字更大,那么只需跳到最大的现有页面,并写入达到该值的其他页面。我可能有一个索引文件,它列出了页面,并有起始/结束素数,这样你就不必读取一堆大文件来执行此检查,但我认为你可以将每个文件中的起始/结束作为第一行,只读取第一行,然后关闭文件。