You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

欧拉计划第8题代码修复咨询:为何小数据正常大数据出错?

解决欧拉计划第8题的溢出问题

嘿,我来帮你揪出这个问题的根源并搞定它!你提到代码处理小数值正常,但面对1000位数字时返回错误结果,猜测是long long的问题——其实核心问题大概率是中间计算时的整数溢出,或者是循环逻辑里的小疏漏,咱们一步步来拆解:

为什么会出现错误结果?

首先算个账:13个9的乘积是9^13 = 2541865828329,这个数值确实在long long的容纳范围内(64位long long最大能到9223372036854775807),理论上long long是够用的。那为什么你的代码会返回离谱的8502282159491788800

大概率是你的代码在计算乘积时,中间步骤用了更小的整数类型(比如int,导致提前溢出。举个例子:如果用int存储单个数字并直接相乘,当多个数字的乘积超过int的最大值(2147483647)时,就会发生溢出,得到错误的中间值,最终整个乘积自然不对。比如9^10 = 3486784401,已经超过了int的上限,这时候用int计算就会溢出,变成负数或者乱码,再赋值给long long也没用了。

另外,暴力循环的写法不仅效率低,也更容易因为重复计算增加出错的概率——比如循环范围写错、漏处理窗口里的0等。

修复方案

1. 强制使用64位整数处理所有乘法步骤

确保所有参与乘法的变量都是long long类型,包括单个数字的存储,避免中间溢出。比如把字符转数字的结果直接存为long long

long long digit = num[i] - '0';

这样每次乘法都是long long之间的运算,不会出现提前溢出的问题。

2. 用滑动窗口优化算法(同时减少出错概率)

暴力循环会重复计算大量重叠的乘积,滑动窗口可以把时间复杂度从O(n*13)降到O(n),同时减少循环次数,降低出错可能。核心思路是:

  • 先计算第一个13位窗口的乘积
  • 滑动窗口时,用当前乘积除以左边离开窗口的数字,再乘以右边进入窗口的新数字
  • 特别注意处理窗口中的0:如果离开的数字是0,需要重新计算当前窗口的乘积(因为除以0会报错,且之前的乘积因为0的存在已经无效)

修复后的示例代码(C++)

#include <iostream>
#include <string>
using namespace std;

int main() {
    // 替换成题目中的1000位数字字符串
    string num = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
    int len = num.size();
    const int window_size = 13;
    long long max_product = 0;

    // 初始化第一个窗口的乘积
    long long current_product = 1;
    int zero_count = 0;
    for (int i = 0; i < window_size; ++i) {
        long long digit = num[i] - '0';
        if (digit == 0) {
            zero_count++;
        } else {
            current_product *= digit;
        }
    }
    if (zero_count == 0) {
        max_product = current_product;
    }

    // 滑动窗口遍历剩余数字
    for (int i = window_size; i < len; ++i) {
        long long left_digit = num[i - window_size] - '0';
        long long right_digit = num[i] - '0';

        if (left_digit == 0) {
            // 左边离开的是0,重新计算当前窗口的乘积
            zero_count--;
            current_product = 1;
            zero_count = 0;
            for (int j = i - window_size + 1; j <= i; ++j) {
                long long d = num[j] - '0';
                if (d == 0) {
                    zero_count++;
                } else {
                    current_product *= d;
                }
            }
        } else {
            // 移除左边数字的影响
            current_product /= left_digit;
            // 添加右边数字的影响
            if (right_digit == 0) {
                zero_count++;
                current_product = 0; // 窗口含0,乘积为0
            } else {
                current_product *= right_digit;
            }
        }

        // 更新最大乘积(跳过含0的窗口)
        if (zero_count == 0 && current_product > max_product) {
            max_product = current_product;
        }
    }

    cout << "最大乘积:" << max_product << endl;
    return 0;
}

代码要点说明

  • 所有数字都转成long long,确保乘法运算全程在64位整数下进行,避免中间溢出
  • zero_count跟踪窗口中的0,避免把含0的无效乘积计入最大值
  • 滑动窗口时,只有当左边离开的数字是0时才重新计算窗口乘积,其他情况直接复用之前的结果,效率更高
  • 最终计算出的结果就是题目要求的正确值(23514624000)

为什么小数值正常?

因为小数值的乘积不会超过int或者long long的范围,中间计算不会溢出,所以结果正确。但面对1000位数字时,某些窗口的乘积在中间步骤会超过小类型的上限,导致溢出,最终得到错误结果。

内容的提问来源于stack exchange,提问作者Math

火山引擎 最新活动