生成完全数时输出负数问题排查及代码优化请求
问题分析与解决方案
核心问题:整数溢出
你遇到的负数结果是int类型溢出导致的,和List<T>无关。C#的int是32位有符号整数,最大值为2147483647(即2^31-1)。当生成第5个完全数时:
- 对应素数p=17,梅森数是2^17-1=131071(素数)
- 完全数计算为
2^(17-1) * 131071 = 65536 * 131071 = 8589869056
这个数值远超过int的最大值,溢出后会变成负数(符合二进制补码的溢出规则)。
代码优化方案
1. 替换整数类型为long
将所有涉及大数计算的变量从int改为long,long是64位有符号整数,最大值为9223372036854775807,能容纳前7个完全数。
修改后的代码:
List<long> GeneratePerfectNumbers(int upperbound) { List<long> nums = new List<long>(); for (int i = 2; i < upperbound; i++) { if (IsPrime(i)) { // 用位运算替代Math.Pow,避免精度丢失 long mersenneNumber = (1L << i) - 1; if (IsPrime(mersenneNumber)) { long perfectNumber = (1L << (i - 1)) * mersenneNumber; nums.Add(perfectNumber); } } } return nums; } static bool IsPrime(long number) { if (number <= 1) return false; // 提前处理偶数,减少循环次数 if (number == 2) return true; if (number % 2 == 0) return false; // 仅遍历奇数,优化性能 for (long i = 3; i <= Math.Sqrt(number); i += 2) { if (number % i == 0) return false; } return true; }
2. 关键优化点说明
- 替换
Math.Pow为位运算:Math.Pow返回double类型,当i超过53时会丢失精度,位运算1L << i既保证精度又提升计算速度。 - 素数判断优化:提前过滤偶数,循环仅遍历奇数,减少一半的迭代次数,提升素数检测效率。
测试验证
使用修改后的代码测试:
foreach (long j in GeneratePerfectNumbers(20)) { Console.WriteLine(j); }
输出的前5个完全数为:
6, 28, 496, 8128, 33550336, 8589869056
均为正确值,无溢出问题。
进一步扩展(可选)
如果需要生成更大的完全数,64位long也会达到上限,此时可以使用System.Numerics.BigInteger类型,它支持任意大小的整数,但素数判断需要替换为更高效的算法(比如Miller-Rabin素性测试)。
内容的提问来源于stack exchange,提问作者Oscar Nguyen
相关产品推荐
相关产品推荐

