大数的素数分解
Posted
技术标签:
【中文标题】大数的素数分解【英文标题】:Prime factorization of large numbers [closed] 【发布时间】:2012-09-03 17:26:09 【问题描述】:我想找到小于 10^12 的。 我得到了这段代码(在 java 中):
public static List<Long> primeFactors(long numbers)
long n = numbers;
List<Long> factors = new ArrayList<Long>();
for (long i = 2; i <= n / i; i++)
while (n % i == 0)
factors.add(i);
n /= i;
if (n > 1)
factors.add(n);
return factors;
首先,上述算法的复杂度是多少??我很难找到它??
对于大数的素数来说,它也太慢了。
有没有更好的算法,或者如何优化这个算法??
【问题讨论】:
对于单个号码,您不会好很多。您可以进行一些预先计算,但这仅在您需要进行大量分解时才有意义。 我会研究 Pollards Rho 算法。您的数字范围实际上并不需要它,但它可能会更好。 可能重复:Prime Factorization Program in Java @harold - 我不确定那是 Pollard 的 Rho。看起来像是从2
到SQRT(n)
的试用部门。我相信SQRT(n)
是正确的:n / i
有点混乱,但我相信循环在i^2 <= n
时执行,相当于i <= SQRT(n)
。内部while
看起来会删除多个素数。例如,如果一个数字是 4 的倍数,它会将 2
添加到列表中两次。
@noloader 是的,这是试用部门,这就是为什么我推荐别的东西
【参考方案1】:
如果你想分解许多个大数,那么你最好先找到直到sqrt(n)
的素数(例如使用Sieve of Eratosthenes)。然后你只需要检查这些素数是否是因子而不是测试所有i <= sqrt(n)
。
【讨论】:
即使我找到了直到sqrt(n)
的所有素数,如果我被要求计算素数分解产生k
素数t
次数的数字,这种方法将性能恶化。你能推荐一些更有效的方法吗?【参考方案2】:
复杂度为O(sqrt(n))
。查sqrt(n)
之后的数字是没有意义的。
这意味着对于10^12
,最多需要1 000 000
次迭代,这并不慢。
【讨论】:
对高达 10^6 的素数进行预计算将使其速度提高约 10 倍【参考方案3】:因式分解大数是一个难题,这也是加密算法使用大素数的因数来使加密难以破解的部分原因。
public static void main(String... args)
int nums = 100;
for (int i = 0; i < nums; i++)
long start = System.nanoTime();
primeFactors(Long.MAX_VALUE - i);
long time = System.nanoTime() - start;
if (time > 100e6)
System.out.println((Long.MAX_VALUE-i) + " took "+time/1000000+" ms.");
public static List<Long> primeFactors(long n)
List<Long> factors = new ArrayList<Long>();
while (n % 2 == 0 && n > 0)
factors.add(2L);
n /= 2;
for (long i = 3; i * i <= n; i+=2)
while (n % i == 0)
factors.add(i);
n /= i;
if (n > 1)
factors.add(n);
return factors;
打印
9223372036854775806 took 3902 ms.
9223372036854775805 took 287 ms.
9223372036854775804 took 8356 ms.
9223372036854775797 took 9519 ms.
9223372036854775796 took 1507 ms.
9223372036854775794 took 111 ms.
9223372036854775788 took 184 ms.
如果您将 Long.MAX_VALUE 替换为 1000000000000L,它们都会在 20 毫秒内分解。
【讨论】:
+1 提到这就是加密算法依赖大素数的原因 自己做优化没有意义(这是编译器的工作)。您可以使用n % 2 == 0
和n /= 2
,这将使代码更具可读性。实际的优化是将i++
替换为i += 2
,因为您已经检查了2 的可分性。预先计算 sqrt(n)
而不是在每次迭代中执行 i*i
也可以产生更好的结果(我已经看到这对 C++ 代码有很大影响)
另外,更重要的是,您应该使用-ea
运行您的代码:如果数字的质因数大于平方根,则断言是错误的:尝试运行primeFactors(6)
。 OP的代码至少是正确的:)
@NiklasB。我的意思是 += 2 :|
我删除了断言,因为它仍然是错误的;)【参考方案4】:
一个更好的算法可能是以下搜索素数(我的 java 生锈了,所以可能需要一些调整来编译它)。
if (number % 2)
factors.append(2)
if (number % 3)
factors.append(3)
for(int n = 0; n < sqrt(number)/6; n++)
if (number % (6 * n + 1))
factors.append(6 * n + 1);
if (number % (6 * n - 1))
factors.append(6 * n - 1);
【讨论】:
不确定这个算法想要达到什么效果! @Nuclearman 为什么要把“sqrt(number/6)”中的数字除以 6? 公平点,看起来应该在平方根之外。 附加因子的条件似乎是互补的。以上是关于大数的素数分解的主要内容,如果未能解决你的问题,请参考以下文章
数学#素数判定Miller_Rabin+大数因数分解Pollard_rho算法 POJ 1811&2429
Prime Test POJ - 1811(素性检测+大数分解)