对 10^18 以内的数进行素因式分解的最快方法

2023-11-21

给定一个数字1 <= n <= 10^18,我怎样才能以最小的时间复杂度分解它?

互联网上有很多帖子讨论如何找到主要因素,但没有一个(至少从我所看到的)说明它们的好处,比如在特定情况下。

除了埃拉托色尼筛法之外,我还使用 Pollard 的 rho 算法:

  • Using sieve, find all prime numbers in the first 107 numbers, and then divide n with these primes as much as possible.
  • 现在使用 Pollard 的 rho 算法尝试找到其余的素数,直到 n 等于 1。

我的实施:

#include <iostream>
#include <vector>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>

using namespace std;

typedef unsigned long long ull;
typedef long double ld;
typedef pair <ull, int> pui;
#define x first
#define y second
#define mp make_pair

bool prime[10000005];
vector <ull> p;

void initprime(){
    prime[2] = 1;
    for(int i = 3 ; i < 10000005 ; i += 2){
        prime[i] = 1;
    }
    for(int i = 3 ; i * i < 10000005 ; i += 2){
        if(prime[i]){
            for(int j = i * i ; j < 10000005 ; j += 2 * i){
                prime[j] = 0;
            }
        }
    }
    for(int i = 0 ; i < 10000005 ; ++i){
        if(prime[i]){
            p.push_back((ull)i);
        }
    }
}

ull modularpow(ull base, ull exp, ull mod){
    ull ret = 1;
    while(exp){
        if(exp & 1){
            ret = (ret * base) % mod;
        }
        exp >>= 1;
        base = (base * base) % mod;
    }
    return ret;
}

ull gcd(ull x, ull y){
    while(y){
        ull temp = y;
        y = x % y;
        x = temp;
    }
    return x;
}

ull pollardrho(ull n){
    srand(time(NULL));
    if(n == 1)
        return n;
    ull x = (rand() % (n - 2)) + 2;
    ull y = x;
    ull c = (rand() % (n - 1)) + 1;
    ull d = 1;
    while(d == 1){
        x = (modularpow(x, 2, n) + c + n) % n;
        y = (modularpow(y, 2, n) + c + n) % n;
        y = (modularpow(y, 2, n) + c + n) % n;
        d = gcd(abs(x - y), n);
        if(d == n){
            return pollardrho(n);
        }
    }
    return d;
}
int main ()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
initprime();
ull n;
cin >> n;
ull c = n;
vector <pui> o;
for(vector <ull>::iterator i = p.begin() ; i != p.end() ; ++i){
    ull t = *i;
    if(!(n % t)){
        o.push_back(mp(t, 0));
    }
    while(!(n % t)){
        n /= t;
        o[o.size() - 1].y++;
    }
}
while(n > 1){
    ull u = pollardrho(n);
    o.push_back(mp(u, 0));
    while(!(n % u)){
        n /= u;
        o[o.size() - 1].y++;
    }
    if(n < 10000005){
        if(prime[n]){
            o.push_back(mp(n, 1));
        }
    }
}
return 0;
}

有没有更快的方法来分解这些数字?如果可以的话,请解释原因并附上源代码。


Approach

Lets say you have a number n that goes up to 1018 and you want to prime factorise it. Since this number can be as small as unity and as big as 1018, all along it can be prime as well as composite, this would be my approach -

  1. Using 米勒·拉宾素性测试,确保该数字是合数。
  2. Factorise n using primes up to 106, which can be calculated using sieve of Eratosthenes.

Now the updated value of n is such that it has prime factors only above 106 and since the value of n can still be as big as 1018, we conclude that the number is either prime or it has exactly two prime factors (not necessarily distinct).

  1. 再次运行 Miller Rabin 以确保该数字不是质数。
  2. Use Pollard rho 算法得到一个素因数。

现在你已经完成了因式分解。

让我们看看上述方法的时间复杂度:

  • 米勒·拉宾接受O(log n)
  • 埃拉托斯特尼筛法O(n*log n)
  • 我分享的 Pollard rho 的实现需要O(n^0.25)

时间复杂度

步骤 2 花费的最大时间等于O(10^7),这又是上述算法的复杂度。这意味着您可以在一秒钟内找到几乎所有编程语言的因式分解。

空间复杂度

空间仅在实施筛子的步骤 2 中使用,并且等于O(10^6)。再次强调,非常实用。

执行

完整代码实施于C++14。该代码有一个隐藏的错误。您可以在下一部分中揭示它,也可以跳到挑战;)

代码中的错误

In line 105,迭代直到i<=np。否则,您可能会错过以下情况prime[np]=999983是一个素因数

挑战

给我一个值n,如果有的话,其中共享代码导致错误的素因数分解。

Bonus

有多少个这样的值n exist ?

Hint

对于这样的 n 值,断言Line 119可能会失败。

Solution

我们打电话吧P=999983。表格中的所有数字n = p*q*r其中 p、q、r 是素数 >=P使得其中至少一个等于P将导致错误的质因数分解。

Bonus Solution

There are exactly four such numbers: {P03, P02P1, P02P2, P0P12}, where P0 = P = 999983, P1 = next_prime(P0) = 1000003, P2 = next_prime(P1) = 1000033.

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

对 10^18 以内的数进行素因式分解的最快方法 的相关文章

  • 如何将无向图转换为 DAG?

    The 维基页面 http en wikipedia org wiki Directed acyclic graph Relation to other kinds of graphs says 任何无向图都可以通过为其顶点选择总顺序并将每
  • 图中的后边

    I m having a hard time understanding Tarjan s algorithm for articulation points I m currently following this tutorial he
  • 有人可以解释以下异或属性

    我的一个论坛提到给定的数组n数字 arr 0 n 1 以下条件成立 is the xor运算符 f l r f 0 r f 0 l 1 where f l r arr l arr l 1 arr r 我检查了上面的数组数量和不同的值l an
  • 时间复杂度和运行时间有什么区别?

    时间复杂度和运行时间有什么区别 它们是一样的吗 运行时间是指程序运行所需的时间 时间复杂度是对输入大小趋于无穷大时运行时间渐进行为的描述 您可以说运行时间 是 O n 2 或其他什么 因为这是描述复杂性类和大 O 表示法的惯用方式 事实上
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 关于在字典中查找所有有效单词的算法问题

    给定一个字典 只是一个字符串列表 您收到来自外部来源的未知数量的信件 给定字母串 您将如何列出您可以通过这些字母的任意组合组成的所有有效单词 来自字典 因此 如果您收到 applead 你应该找到apple bad pad lead等 我知
  • 如何仅使用单个数组在 JavaScript 中模拟调用堆栈

    我正在看维基百科页面 https en wikipedia org wiki Call stack在调用堆栈上 并尝试理解这个图像 据我所知 哈哈 const memory memory 0 3 top of stack pointer m
  • 生成所有多集大小为 n 的分区的算法

    我一直在试图找出一种方法来生成多重集的所有不同的大小为 n 的分区 但到目前为止却空手而归 首先让我展示一下我想要实现的目标 假设我们有一个输入向量uint32 t std vector
  • 使用并集查找(又名不相交集)检测图是否是二分图

    我正在 Spoj 上做一个问题 基本上可以简化为检测图是否是二分图 我正在尝试使用 dfs 为图表着色 但它太慢了 有人评论这个 没有 bfs 没有 dfs 没有二部图 简单的并查集就可以做到 确实速度很快 提示 1 偶数长度的环不会影响两
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 绘制多边形

    我正在使用 Google Maps API V3 根据路径绘制多边形 该路径是随机未排序坐标点 LatLng 的数组 这会产生以下形状 Polylines intersect Problem 由于多边形的形状取决于路径中点的顺序 因此如何对
  • 给定一个具有多个重复条目的数组,找到一个重复条目 O(N) 时间和常数空间

    我们得到了一个大小为 N 的数组 其中包含 0 到 N 2 范围内的整数 包括 0 和 N 2 该数组可以有多个重复的条目 我们需要在 O N 时间和常量空间中找到重复条目之一 我正在考虑取数组中所有条目的乘积和总和 以及 0 到 N 2
  • 如何从迭代器推导连续内存

    不知何故 本土stl copy VC Dinkumware 上的算法表明它可以使用memcpy 可以轻松复制的数据 一个凡人能做到这一点吗 假设每个元素都是普通可复制的 random access iterator 是否意味着连续内存 标准
  • 归并排序中递归树的高度log(n)+1是怎么来的

    我按照 stackoveflow 的建议阅读了一些问题和答案 我正在遵循 cormen 的 算法简介 一书进行自学 那本书里已经解释得很清楚了 但唯一没有解释的是如何在合并排序分析中计算树的高度 如果在后面的章节中对此进行解释的话 我仍然在
  • 实施二分查找有哪些陷阱? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 二分查找比看起来更难实现 虽然二分搜索的基本思想相对简单 但细节可能出人意料地棘手 Donald Knuth 新的二分搜索实现中最有可
  • 照片马赛克算法。如何在给定基本图像和瓷砖列表的情况下创建马赛克照片?

    Hy 我要做的是创建一个程序 使用 C 或 C 它将 24 位 像素位图和图像集合作为输入 我必须创建一个马赛克图像 类似于使用库的输入图像给定的图像 创建与输入类似的马赛克照片 到目前为止 我可以访问输入的图像像素及其颜色 但我有点卡住了
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • 应用对数来导航树

    我曾经知道一种使用对数从树的一片叶子移动到树的下一个 有序 叶子的方法 我认为它涉及获取 当前 叶子的位置值 排名 并将其用作从根向下到新目标叶子的新遍历的种子 一直使用对数函数测试来确定是否沿着右或左节点向下到达叶子 我已经不记得如何运用

随机推荐