BigInteger 数字的实现和性能

2024-04-19

我用 C++ 编写了一个 BigInteger 类,它应该能够对任何大小的所有数字进行运算。目前,我正在尝试通过比较现有算法并测试它们最适合哪些位数来实现非常快速的乘法方法,但我遇到了非常意外的结果。我尝试进行 20 次 500 位数字的乘法,并对它们进行计时。结果是这样的:

karatsuba:
  14.178 seconds

long multiplication:
  0.879 seconds

维基百科告诉我

由此可见,对于足够大的 n,Karatsuba 算法将比普通乘法执行更少的移位和个位数加法,尽管其基本步骤比直接公式使用更多的加法和移位。然而,对于较小的 n 值,额外的移位和加法操作可能会使其运行速度比普通方法慢。正回报点取决于计算机平台和环境。根据经验,当被乘数长于 320-640 位时,Karatsuba 通常会更快。

由于我的数字至少有 1500 位长,这是非常意外的,因为维基百科说 karatsuba 应该运行得更快。我相信我的问题可能出在我的加法算法中,但我不知道如何使其更快,因为它已经在 O(n) 中运行。我将在下面发布我的代码,以便您可以检查我的实现。我将省略不相关的部分。
我也在想,也许我使用的结构不是最好的。我用小端表示每个数据段。例如,如果我将数字 123456789101112 存储到长度为 3 的数据段中,它将如下所示:

{112,101,789,456,123}

所以这就是为什么我现在要问实现 BigInteger 类的最佳结构和最佳方法是什么?为什么 karasuba 算法比长乘法慢?

这是我的代码:(对于长度我很抱歉)

using namespace std;

bool __longmult=true;
bool __karatsuba=false;

struct BigInt {
public:
    vector <int> digits;

    BigInt(const char * number) {
        //constructor is not relevant   
    }
    BigInt() {}

    void BigInt::operator = (BigInt a) {
        digits=a.digits;
    }

    friend BigInt operator + (BigInt,BigInt);
    friend BigInt operator * (BigInt,BigInt);

    friend ostream& operator << (ostream&,BigInt);
};

BigInt operator + (BigInt a,BigInt b) {
    if (b.digits.size()>a.digits.size()) {
        a.digits.swap(b.digits); //make sure a has more or equal amount of digits than b
    }
    int carry=0;

    for (unsigned int i=0;i<a.digits.size();i++) {
        int sum;
        if (i<b.digits.size()) {
            sum=b.digits[i]+a.digits[i]+carry;
        } else if (carry==1) {
            sum=a.digits[i]+carry;
        } else {
            break; // if carry is 0 and no more digits in b are left then we are done already
        }

        if (sum>=1000000000) {
            a.digits[i]=sum-1000000000;
            carry=1;
        } else {
            a.digits[i]=sum;
            carry=0;
        }
    }

    if (carry) {
        a.digits.push_back(1);
    }

    return a;
}

BigInt operator * (BigInt a,BigInt b) {
    if (__longmult) {
        BigInt res;
        for (unsigned int i=0;i<b.digits.size();i++) {
            BigInt temp;
            temp.digits.insert(temp.digits.end(),i,0); //shift to left for i 'digits'

            int carry=0;
            for (unsigned int j=0;j<a.digits.size();j++) {
                long long prod=b.digits[i];
                prod*=a.digits[j];
                prod+=carry;
                int t=prod%1000000000;
                temp.digits.push_back(t);
                carry=(prod-t)/1000000000;
            }
            if (carry>0) {
                temp.digits.push_back(carry);
            }
            res+=temp;
        }
        return res;
    } else if (__karatsuba) {
        BigInt res;
        BigInt a1,a0,b1,b0;
        assert(a.digits.size()>0 && b.digits.size()>0);
        while (a.digits.size()!=b.digits.size()) { //add zeroes for equal size
            if (a.digits.size()>b.digits.size()) {
                b.digits.push_back(0);
            } else {
                a.digits.push_back(0);
            }
        }

        if (a.digits.size()==1) {
            long long prod=a.digits[0];
            prod*=b.digits[0];

            res=prod;//conversion from long long to BigInt runs in constant time
            return res;

        } else {
            for (unsigned int i=0;i<a.digits.size();i++) {
                if (i<(a.digits.size()+(a.digits.size()&1))/2) { //split the number in 2 equal parts
                    a0.digits.push_back(a.digits[i]);
                    b0.digits.push_back(b.digits[i]);
                } else {
                    a1.digits.push_back(a.digits[i]);
                    b1.digits.push_back(b.digits[i]);
                }
            }
        }

        BigInt z2=a1*b1;
        BigInt z0=a0*b0;
        BigInt z1 = (a1 + a0)*(b1 + b0) - z2 - z0;

        if (z2==0 && z1==0) {
            res=z0;
        } else if (z2==0) {
            z1.digits.insert(z1.digits.begin(),a0.digits.size(),0);
            res=z1+z0;
        } else {
            z1.digits.insert(z1.digits.begin(),a0.digits.size(),0);
            z2.digits.insert(z2.digits.begin(),2*a0.digits.size(),0);
            res=z2+z1+z0;
        }

        return res;
    }
}

int main() {
    clock_t start, end;

    BigInt a("984561231354629875468546546534125215534125215634987498548489456125215421563498749854848945612385663498749854848945612521542156349874985484894561238561698774565123165221393856169877456512316552156349874985484894561238561698774565123165221392213935215634987498548489456123856169877456512316522139521563498749854848945612385616987745651231652213949651465123151354686324848945612385616987745651231652213949651465123151354684132319321005482265341252156349874985484894561252154215634987498548489456123856264596162131");
    BigInt b("453412521563498749853412521563498749854848945612521542156349874985484894561238565484894561252154215634987498548489456123856848945612385616935462987546854521563498749854848945653412521563498749854848945612521542156349874985484894561238561238754579785616987745651231652213965465341235215634987495215634987498548489456123856169877456512316522139854848774565123165223546298754685465465341235215634987498548354629875468546546534123521563498749854844139496514651231513546298754685465465341235215634987498548435468");

    __longmult=false;
    __karatsuba=true;

    start=clock();
    for (int i=0;i<20;i++) {
        a*b;
    }
    end=clock();
    printf("\nTook %f seconds\n", (double)(end-start)/CLOCKS_PER_SEC);

    __longmult=true;
    __karatsuba=false;

    start=clock();
    for (int i=0;i<20;i++) {
        a*b;
    }
    end=clock();
    printf("\nTook %f seconds\n", (double)(end-start)/CLOCKS_PER_SEC);

    return 0;
}

  1. 您使用 std::vector

    对于您的数字,请确保其中没有不必要的重新分配。所以在操作前分配空间以避免这种情况。另外我不使用它,所以我不知道数组范围检查速度减慢。

    检查一下你是否没有转移它!这是O(N)...即插入到第一个位置...

  2. 优化您的实施

    here https://stackoverflow.com/q/18465326/2521214你可以找到我的实现优化和未优化的比较

    x=0.98765588997654321000000009876... | 98*32 bits...
    mul1[ 363.472 ms ]... O(N^2) classic multiplication
    mul2[ 349.384 ms ]... O(3*(N^log2(3))) optimized karatsuba multiplication
    mul3[ 9345.127 ms]... O(3*(N^log2(3))) unoptimized karatsuba multiplication 
    

    Karatsuba 的我的实现阈值约为 3100 位... ~ 944 位!情人阈值代码越优化。


    尝试从函数操作数中删除不必要的数据

    //BigInt operator + (BigInt a,BigInt b)
    BigInt operator + (const BigInt &a,const BigInt &b)
    

    这样您就不会创建另一个副本a,b在每个 + 调用的堆上也更快的是:

    mul(BigInt &ab,const BigInt &a,const BigInt &b) // ab = a*b
    
  3. 舍恩哈格-施特拉森乘法

    这个是FFT or NTT基于。我的阈值很大... ~ 49700bits ... ~ 15000digits 所以如果你不打算使用这么大的数字那就忘记它吧。实施也在上面的链接中。


    here https://stackoverflow.com/q/18577076/2521214是我的NTT实施(尽我所能优化)

  4. Summary

    使用小端或大端并不重要,但您应该以不使用插入操作的方式对操作进行编码。


    您使用十进制基数来表示速度很慢,因为您需要使用除法和模运算。如果你选择底数为 2 的幂那么只需位操作就足够了而且它还从代码中删除了许多 if 语句,这些语句最慢。如果您需要 10 的幂作为底数,那么在某些情况下可以使用最大的值,这样可以将 div,mod 减少到很少的减法

    2^32 = 4 294 967 296 ... int = +/- 2147483648
    base = 1 000 000 000
    
    //x%=base
    while (x>=base) x-=base;
    

    在某些平台上,最大周期数是 2^32/base 或 2^31/base,这比模数更快,而且基数越大,需要的操作就越少,但要注意溢出!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BigInteger 数字的实现和性能 的相关文章

  • n 或 nlog(n) 比常数时间或对数时间更好吗?

    在 Coursera 上的普林斯顿教程中 讲师解释了遇到的常见增长顺序函数 他说 线性和线性算术运行时间是 我们努力的目标 他的推理是 随着输入大小的增加 运行时间也会增加 我认为这是他犯了错误的地方 因为我之前听过他提到线性增长顺序对于高
  • 当我使用“control-c”关闭发送对等方的套接字时,为什么接收对等方的套接字不断接收“”

    我是套接字编程的新手 我知道使用 control c 关闭套接字是一个坏习惯 但是为什么在我使用 control c 关闭发送进程后 接收方上的套接字不断接收 在 control c 退出进程后 发送方的套接字不应该关闭吗 谢谢 我知道使用
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • 为什么 Delphi 中的 ADO Next 记录处理速度变慢?

    我有一个多年前开发的 Delphi 4 程序 它使用Opus 直接访问 http sourceforge net projects directaccess 按顺序搜索 Microsoft Access 数据库并检索所需的记录 Delphi
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • 加快网络抓取速度

    我正在使用一个非常简单的网络抓取工具抓取 23770 个网页scrapy 我对 scrapy 甚至 python 都很陌生 但设法编写了一个可以完成这项工作的蜘蛛 然而 它确实很慢 爬行 23770 个页面大约需要 28 小时 我看过scr
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 二维滑动窗口最小值/最大值

    假设我们得到一个大小为 NxN 的像素整数矩阵和一个整数 k 窗口大小 我们需要使用滑动窗口找到矩阵中的所有局部最大值 或最小值 这意味着 如果某个像素与其周围窗口中的所有像素相比具有最小 最大 值 则应将其标记为最小 最大 有一种著名的滑
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框

随机推荐

  • php 获取包含文件的名称空间

    file foo php file index php 我的问题是 从我的 index php 文件中 是否可以知道 foo php 的命名空间是什么 而无需读取文件内容并对其执行正则表达式 这似乎是一个很大的开销 EDIT 我真的希望能够
  • 如何将图像插入 Latex 格式的 Anki 笔记中?

    我正在尝试创建一个 Anki 牌组 例如 前面有一个单词 然后我在后面添加带有定义的单词以及图片 但是当已经有两个字段 前面 的文本和后面的文本 时 我在包含图形时遇到了麻烦 这是一个注释示例 begin note begin field
  • 仅当针对较低 API 时才在 Android M 上请求权限

    因此 在我的应用程序中 我想添加一个选项 以便当用户使用 Android M 时有选择地添加权限 例如 直接拨号 但同时 我希望该权限不会按照 API 22 中的要求显示或更低只是因为它不是必需的 所以我宁愿在安装过程中不要求它 因此事实上
  • 需要有关 Enumerable.Aggregate 函数的更多详细信息

    你能帮我理解吗 words Aggregate workingSentence next gt next workingSentence 从下面的代码片段 如果有人解释我如何在 C 1 1 中实现这一点 那就太好了 摘自MS http ms
  • 相当于.net中的SoftReference?

    我熟悉WeakReference 但我正在寻找一个已清除的引用类型only当内存不足时 不仅仅是每次运行 gc 时 就像 Java 的SoftReference 我正在寻找一种实现内存敏感缓存的方法 ASP NET 缓存为您提供了所需的内存
  • 导出图像格式的访问图表?

    我在 Access 表单中创建了一个图表 并将其以图像格式导出 这很容易完成 但是当我关闭表单时 问题就出现了 它显示了一条弹出消息 对图表对象的操作失败 OLE 服务器可能未注册 要注册 OLE 服务器 请重新安装它 然后我做了一些改变
  • JS/AJAX:使用计时器提交表单,而不是单击按钮或刷新页面

    我正在尝试提交没有页面刷新或提交按钮的表单 但我只实现了让JS函数提交输入框值 是否可以在不单击按钮和刷新页面的情况下提交整个表单 JSFIDDLE http jsfiddle net MswhY 8 JS
  • 使用python统计lmdb数据库中的记录数

    我打开一个lmdb使用此代码的数据库 lmdb env lmdb open source path readonly True 如何计算该数据库中的记录数 我认为应该是这样的 lmdb env lmdb open lmdb file nam
  • 检测默认网络浏览器的代理设置

    MSDN样本 HttpWebRequest myWebRequest HttpWebRequest WebRequest Create http www microsoft com WebProxy myProxy new WebProxy
  • JSON 的 XSLT 等效项

    有没有一个XSLT http www w3 org TR xslt相当于 JSON 允许我对 JSON 进行转换 就像 XSLT 对 XML 所做的那样 JSON 的 XSLT 等效项 候选列表 工具和规范 Tools 1 XSLT htt
  • SceneBuilder 不会加载通过 FXML 引用另一个自定义控件的自定义控件

    我创建了一个基于 FXML 的自定义控件 该控件又引用另一个基于 FXML 的自定义控件 当我在 Eclipse 中加载它们时 它们都工作得很好 但是当我尝试将它们导入到 SceneBuilder 中时 外部控件 包含另一个控件的一个 无法
  • PHP 中短代码的正则表达式模式

    我编写的用于匹配 PHP 中的短代码的正则表达式有问题 这是模式 其中 shortcode是短代码的名称 shortcode shortcode 现在 这个正则表达式对于这些格式表现得非常好 shortcode shortcode valu
  • 为什么不能在 svg 路径元素上使用transform:translateZ?

    我想对内联 svg 元素的部分进行动画处理 我认为你可以在 svg 路径元素上使用 css 转换 从而为 svg 的部分设置动画 这真的很酷 但在使用它之后 我遇到了 translateZ 函数的问题 由于某种原因 在路径元素上使用第三维似
  • 如何删除材料设计按钮中的额外填充或边距?

    我正在尝试创建一个附加到按钮上方的 TextView 的按钮 如下图所示 上面的截图取自Note 4 操作系统版本为5 0 1 下面是用于实现UI的代码 布局 xyz xml
  • C++ 编码指南 102

    如果您被允许在 101 条准则中添加另一个编码准 则 C 编码标准 Herb Sutter 和 Andrei Alexandrescu http www gotw ca publications c cs htm 您会添加哪一个 一年后再写
  • 模板什么时候结束?

    模板什么时候结束 我们来看看这段代码 template
  • 带有 vararg observables 的 RxJava zip

    当我们确切地知道有多少个具有确切类型的可观察量并且我们想要压缩时 我们会这样做 Observable
  • JetBrains IDE 启动时出错:应用程序无法正确启动 (0xc000007b)

    我遇到了这个错误 但在重新安装 IDE 两次后几乎找不到解决方案 甚至我安装了 多合一运行时 但这也无济于事 因为我认为问题最初是在我更改了 Windows Defender 设置中的一些设置后开始的然后尝试重置它们 但肯定其他人报告了这个
  • lambda:通过引用捕获 const 引用是否应该产生未定义的行为?

    我刚刚在代码中发现了一个令人讨厌的错误 因为我通过引用捕获了对字符串的 const 引用 当 lambda 运行时 原始字符串对象已经消失了 引用的值是空的 而目的是它包含原始字符串的值 因此出现了错误 让我困惑的是 这并没有在运行时引发崩
  • BigInteger 数字的实现和性能

    我用 C 编写了一个 BigInteger 类 它应该能够对任何大小的所有数字进行运算 目前 我正在尝试通过比较现有算法并测试它们最适合哪些位数来实现非常快速的乘法方法 但我遇到了非常意外的结果 我尝试进行 20 次 500 位数字的乘法