我有一个采用上限的方法,并返回一个质数列表,直到该限制。
public static List<int> AllPrimesUnder(int upperLimit)
后来我决定我真的只需要在列表上进行查找,通常只是问“Is This Prime”这个问题。由于我正在处理像一百万这样的所有素数,我意识到 HashSet 是我应该使用的结构。当然,使用方法结果的查找速度更快,但方法本身的速度较慢。
我相信它变慢的原因是因为 HashSet 在添加之前检查重复项,而 List 只是将它推到最后。令我惊讶的是,以及产生问题和标题的原因是为什么从 List 开始并使用它来创建 HashSet,如下所示:
hashSet = new HashSet<int>(Prime.AllPrimesUnder(1000000));
比在方法内部使用 Hashset 更快,启用如下调用:
hashSet = Prime.AllPrimesUnder_Hash(1000000);
如果减速是在重复检查中,无论如何它都应该进行相同数量的检查,对吧?这可能是我的理解失败的地方。
这是我得到一百万以下素数的时间。
- 0.1136s 纯哈希
- 0.0975s 纯列表(预计更快)
- 0.0998s 纯列表转换为哈希(未预期)
如果可以简单地解释其原因,我很想听听。我想至少我正在寻找的东西足以了解我是否应该从 List 或 HashSet 开始,如果最终结果将是一个大的 HashSet 项目。
我在下面添加了主要方法的主体,但请注意,与数据结构的所有交互在两者之间都是相同的(代码方面)。我不相信我如何将数据添加到结构中会影响异常。
public static List<int> AllPrimesUnder(int upperLimit)
{
List<int> primeList = new List<int>();
primeList.Add(2);
int testNumber = 3;
bool isPrime;
while (testNumber <= upperLimit)
{
isPrime = true;
foreach (int prime in primeList)
{
if (testNumber % prime == 0)
{
isPrime = false;
break;
}
if (testNumber < prime*prime)
break;
}
if (isPrime)
primeList.Add(testNumber);
testNumber++;
}
return primeList;
}
编辑:根据要求,我正在添加 Hash 方法的代码。如果它看起来几乎相同,那是因为它是。
public static HashSet<int> AllPrimesUnder_Hash(int upperLimit)
{
HashSet<int> primeHash = new HashSet<int>();
primeHash.Add(2);
int testNumber = 3;
bool isPrime;
while (testNumber <= upperLimit)
{
isPrime = true;
foreach (int prime in primeHash)
{
if (testNumber % prime == 0)
{
isPrime = false;
break;
}
if (testNumber < prime*prime)
break;
}
if (isPrime)
primeHash.Add(testNumber);
testNumber++;
}
return primeList;
}
还应要求提供我用来测试执行时间的(丑陋的hackish)代码:
Stopwatch stopWatch = new Stopwatch();
int iterations = 1;
HashSet<int> hashSet = new HashSet<int>();
List<int> list = new List<int>();
stopWatch.Restart();
for (int i = 0; i < iterations; i++)
{
hashSet = Prime.AllPrimesUnder_Hash(1000000);
}
stopWatch.Stop();
Console.WriteLine("Hash: " + (stopWatch.Elapsed.TotalSeconds / iterations).ToString("#.###################"));
///////////////////////
stopWatch.Restart();
for (int i = 0; i < iterations; i++)
{
hashSet = new HashSet<int>(Prime.AllPrimesUnder(1000000));
}
stopWatch.Stop();
Console.WriteLine("List converted: " + (stopWatch.Elapsed.TotalSeconds / iterations).ToString("#.###################"));