Karatsuba整数乘法失败,出现分段错误

时间:2017-04-14 06:51:35

标签: c++ karatsuba

当我运行该程序时,它会因分段错误而崩溃。此外,当我在codeblocks IDE中调试代码时,我也无法调试它。程序在调试开始之前就崩溃了。我无法理解这个问题。任何帮助,将不胜感激。谢谢!

AWS_STORAGE_BUCKET_NAME = ''
AWS_ACCESS_KEY_ID = ''
AWS_SECRET_ACCESS_KEY = ''
MEDIA_URL = 'http://%s.s3.amazonaws.com/your-folder/'
DEFAULT_FILE_STORAGE = "storages.backends.s3boto.S3BotoStorage"

1 个答案:

答案 0 :(得分:1)

您的代码中存在三个严重问题:

  1. result = (char)carry + result;不起作用。
    进位值介于0(0 * 0)和8(9 * 9)之间。必须将其转换为相应的ASCII值:
    result = (char)(carry + '0') + result;

  2. 这会导致下一个问题:如果是0,则偶数也会被插入。缺少if声明:
    if (carry/* != 0*/) result = (char)(carry + '0') + result;

  3. 修复前两个问题并再次测试后,仍然会发生堆栈溢出。所以,我将你的算法与google发现的另一个算法进行了比较:
    Divide and Conquer | Set 4 (Karatsuba algorithm for fast multiplication)
    (可能是你的起源,因为它看起来非常相似)。在没有深入挖掘的情况下,我修复了看似简单的转移错误:return P1 * pow(10, 2 * sh) + P2 * pow(10, sh) + P3;
    (我将length替换为2 * shlength/2替换为sh就像我在googled代码中看到的那样。)这对于我在调试器中看到长度可以具有奇数值以使shlength/2是不同的值变得显而易见。

  4. 之后,您的计划开始运作。

    我更改了main()功能,以便更加努力地测试它:

    #include <cmath>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    string intToStr(int i)
    {
      string text;
      do {
        text.insert(0, 1, i % 10 + '0');
        i /= 10;
      } while (i);
      return text;
    }
    
    // Method to make strings of equal length
    int makeEqualLength(string &fnum, string &snum)
    {
      int l1 = (int)fnum.length();
      int l2 = (int)snum.length();
      return l1 < l2
        ? (fnum.insert(0, l2 - l1, '0'), l2)
        : (snum.insert(0, l1 - l2, '0'), l1);
    }
    
    int singleDigitMultiplication(const string& fnum, const string& snum)
    {
      return ((fnum[0] - '0') * (snum[0] - '0'));
    }
    
    string addStrings(string& s1, string& s2)
    {
      int length = makeEqualLength(s1, s2);
      int carry = 0;
      string result;
      for (int i = length - 1; i >= 0; --i) {
        int fd = s1[i] - '0';
        int sd = s2[i] - '0';
        int sum = (fd + sd + carry) % 10 + '0';
        carry = (fd + sd + carry) / 10;
        result.insert(0, 1, (char)sum);
      }
      if (carry) result.insert(0, 1, (char)(carry + '0'));
      return result;
    }
    
    long int multiplyByKaratsubaMethod(string fnum, string snum)
    {
      int length = makeEqualLength(fnum, snum);
      if (length == 0) return 0;
      if (length == 1) return singleDigitMultiplication(fnum, snum);
    
      int fh = length / 2;
      int sh = length - fh;
    
      string Xl = fnum.substr(0, fh);
      string Xr = fnum.substr(fh, sh);
      string Yl = snum.substr(0, fh);
      string Yr = snum.substr(fh, sh);
    
      long int P1 = multiplyByKaratsubaMethod(Xl, Yl);
      long int P3 = multiplyByKaratsubaMethod(Xr, Yr);
      long int P2
        = multiplyByKaratsubaMethod(addStrings(Xl, Xr), addStrings(Yl, Yr))
        - P1 - P3;
      return P1 * pow(10, 2 * sh) + P2 * pow(10, sh) + P3;
    }
    
    int main()
    {
      int nErrors = 0;
      for (int i = 0; i < 1000; i += 3) {
        for (int j = 0; j < 1000; j += 3) {
          long int result
            = multiplyByKaratsubaMethod(intToStr(i), intToStr(j));
          bool ok = result == i * j;
          cout << i << " * " << j << " = " << result
            << (ok ? " OK." : " ERROR!") << endl;
          nErrors += !ok;
        }
      }
      cout << nErrors << " error(s)." << endl;
      return 0;
    }
    

    关于我所做的更改的说明:

    1. 关于std库:请不要将标题与“.h”混合使用。 std库的每个标题都以“非后缀风格”提供。 (带有“.h”的标题是C标题或老式标题。)C库的标题已经适应了C ++。它们的旧名称带有前缀“c”,没有后缀“.h” 因此,我将#include <math.h>替换为#include <cmath>

    2. 我忍不住让makeEqualLength()缩短一点。

    3. 请注意,std中的许多方法都使用std::size_t而不是intunsignedstd::size_t具有适当的宽度来进行数组下标和指针算术,即它具有“机器字宽”。我相信很长一段时间intunsigned也应该有“机器字宽”并且不关心size_t。当我们在Visual Studio中从x86(32位)更改为x64(64位)时,我学到了很难的错误:std::size_t现在是64位但是int和{{1仍然是32位。 (MS VC ++也不例外。其他编译器供应商(但不是全部)以相同的方式执行。))我插入了一些C类型转换来从编译器输出中删除警告。应该始终小心使用这些用于删除警告的强制转换(无论您使用C强制转换还是更好的C ++强制转换),都应该被理解为确认:亲爱的编译器。我看到你有顾虑,但我(相信)知道并向你保证它应该可以正常工作。

    4. 我不确定您是否打算在某些地方使用unsigned。 (可能是您从原始来源转移了此代码而没有关心。)正如您所知,所有long int类型的实际大小可能不同,以匹配目标平台的最佳性能。我正在使用Visual Studio在带有Windows 10的Intel-PC上工作。 int(32位)。无论我编译x86代码(32位)还是x64代码(64位),这都是独立的。对于gcc(在我的情况下是cygwin)以及任何带有Linux(AFAIK)的Intel-PC,情况都是如此。对于授予比sizeof (int) == sizeof (long int)更大的类型,您必须选择int

    5. 我在Windows 10(64位)上的cygwin中进行了示例会话:

      long long int

      等。等

      $ g++ -std=c++11 -o karatsuba karatsuba.cc 
      
      $ ./karatsuba
      0 * 0 = 0 OK.
      0 * 3 = 0 OK.
      0 * 6 = 0 OK.