我得到一个整数列表(最多1000个),它乘以给定的整数n
。
我需要在整数n
的所有除数中找到最高的幂。
例如:4,7,8乘以224,最高功率则为5,因为224 = 2 ^ 2 * 7 * 2 ^ 3 = 2 ^ 5 * 7.
问题是,1000个整数可能大到2 ^ 64,因此n
非常大。
解决此问题的优秀算法是什么?
答案 0 :(得分:6)
困难。我首先开始检查小素数(在你的例子中:4,7,8。产品有2 ^ 5的因子。你除以2的幂,留下1,7,1。然后你做同样的3 ,5,7等等到X)。
现在你需要找到更大的素数p> X比你发现的最高功率更高。花费大量时间来找到仅发生一次的主要因素似乎是浪费。您需要具有多个数字因子的素数。计算每对数字的gcd并查看这些数字的素数因子。有很多细节需要解决,这就是我开始“困难”的原因。
最坏的情况是你不能轻易找到任何两次出现的素数,所以你需要检查每个数字是否包含素数的平方因子。
作为一个例子:你检查了多达1000的因子,你发现一个素数的最高功率是83 ^ 3。所以现在你需要找到一个更大的素数,这是第四种力量或显示没有。计算成对gcd(最大公约数)。一个大素数可能是这些gcd的多个来自四个不同数字的除数,或者p可能是三个gcd的因子,p ^ 2是一个数字的因子等。
澄清原则:假设你有两个巨大的数字x和y,你想知道哪个是分割xy的素数的最高幂。你可以将x和y因子分解并从那里开始。如果它们都是素数或两个大素数的乘积,比如x = p或pq,y = r或rs,这需要很长时间。
现在计算x和y的gcd。如果最大公约数是z> 1,则z是x和y的因子,因此z ^ 2是xy的因子。如果最大公约数是1,那么x和y没有共同因子。由于我们不需要不是正方形的因子,我们寻找正方形和更高的因子。为此,您只需要将因子除以x ^(1/3)或y ^(1/3)。
答案 1 :(得分:0)
为什么不在评论中找到@SamVarshavchik建议的每个数字的素数分解?对于OP正在处理的数字(即最多2 64 ),使用经典筛分方法肯定会永远占用,但是这些数字完全在算法范围内Pollard's Rho algorithm 。从问题What is the fastest factorization algorithm的答案中,我们可以看到Pollard的Rho算法开始在2 70 范围内分解,所以我们应该没问题。已有多个已发布的C++
实施版可供使用,但我建议将下面的Python
代码转换为C++
。它是原始算法的一种更快的变体,由Richard Brent于1980年出版(代码发现here)
def brent(N):
if N%2==0:
return 2
y,c,m = random.randint(1, N-1),random.randint(1, N-1),random.randint(1, N-1)
g,r,q = 1,1,1
while g==1:
x = y
for i in range(r):
y = ((y*y)%N+c)%N
k = 0
while (k<r and g==1):
ys = y
for i in range(min(m,r-k)):
y = ((y*y)%N+c)%N
q = q*(abs(x-y))%N
g = gcd(q,N)
k = k + m
r = r*2
if g==N:
while True:
ys = ((ys*ys)%N+c)%N
g = gcd(abs(x-ys),N)
if g>1:
break
return g
一旦我们得到每个数的素数因子分解,我们将每个数的所有素因子组合成一个向量,对向量进行排序,计算每个素数的出现次数,最后,我们将发生的那些素因子结合起来最多次成为一个除数。
要使此算法起作用,您需要一个快速测试素数的函数(我建议使用Miller-Rabin primality test)。此功能将在前线用作过滤器,以免浪费时间在Pollard Rho功能中。我用几种语言手动编写了上述所有算法,并且可以证明这是一项非常简单的任务。
为了向您展示这将达到预期的效果,我在下面编写了一个更糟糕的案例场景,提供了R
代码(转换为C++
应该即时运行),该代码在分配的15秒内运行良好。 factorize
函数来自gmp
包,并实现了标准的Pollard的Rho算法。当我说“最坏情况”时,我的意思是列表中的所有1000个数字都是半素数(这对于Pollard的Rho算法来说是最长的因素)非常接近2 64 限制。如果给定的数字只是小于2 64 的1000个随机数,则下面的算法将在不到几秒的时间内运行,因为具有小素数的数的概率非常大(这个是Pollard的Rho算法的真正力量所在。)
KindergartenGraduation <- function(passTime) {
BegTime <- Sys.time()
set.seed(11)
Samp_NoRep <- nextprime(sample(2^32, 2000, replace = FALSE)) ## generate 2000 random primes
## pick 2000 primes from the sample above with replacement
## so as to guarantee a non-trivial answer
Samp_WiRep <- sample(Samp_NoRep, 2000, replace = TRUE)
## Below, we multiply each odd element with its neighbor
## to create large semi-primes
## all odd indices ## and the even ones
TestNums <- mul.bigz(Samp_WiRep[seq.int(1,2000,2)], Samp_WiRep[seq.int(2,2000,2)])
## finding the prime factorization should be the slowest step
BegFacTime <- Sys.time()
MyPriFacs <- do.call(c, lapply(TestNums, factorize))
EndFacTime <- Sys.time()
TotFacTime <- EndFacTime - BegFacTime
myFacTime <- strsplit(format(TotFacTime), split = " ")[[1]]
timeFacNum <- myFacTime[1]
timeFacUnit <- myFacTime[2]
print(paste(c("Factoring list of large integers took ",timeFacNum," ",timeFacUnit), collapse = ""))
## sorting in order to apply run length encoding
MyPriFacs <- MyPriFacs[order(asNumeric(MyPriFacs))]
MyRle <- function (x1) {
n1 <- length(x1)
y1 <- x1[-1L] != x1[-n1]
i <- c(which(y1), n1)
list(lengths = diff(c(0L, i)), values = x1[i], uni = sum(y1)+1L)
}
BreakDown <- MyRle(MyPriFacs)
## Find maximum power
MyMax <- max(BreakDown$lengths)
## Determine which prime(s) occur(s) the maximum number of times
MaxRep <- which(BreakDown$lengths == MyMax)
## If we find multiple, we combine them to obtain the single divisor with the maximum power
MyAns <- list(primes = BreakDown$values[MaxRep], divisor = prod.bigz(BreakDown$values[MaxRep]), largest_power = MyMax)
EndTime <- Sys.time()
TotTime <- EndTime - BegTime
myTime <- strsplit(format(TotTime), split = " ")[[1]]
timeNum <- myTime[1]
timeUnit <- myTime[2]
print(paste(c("You found the most frequent divisor in ",timeNum," ",timeUnit), collapse = ""))
if (as.numeric(timeNum) < passTime) {
print("Yay!!! I graduated Kindergarten on Earth 2.0 and I'm now ready to build my first quantum computer!!!")
} else {
print("Oh no!!! I failed Kindergarten and have to recite K&R 10^100 more times!!!")
}
return(MyAns)
}
打电话给我们:
KindergartenGraduation(15)
[1] "Factoring list of large integers took 9.417561 secs"
[1] "You found the most frequent divisor in 9.492585 secs"
[1] "Yay!!! I graduated Kindergarten on Earth 2.0 and I'm now ready to build my first quantum computer!!!"
$primes
Big Integer ('bigz') object of length 4:
[1] 243258997 335592419 2615771369 3202455203
$divisor
Big Integer ('bigz') :
[1] 683854798468133589409768665696700901
$largest_power
[1] 5
正如您所看到的,素数:243258997, 335592419, 2615771369, 3202455203
最常发生在每件5次,因此这些数字的乘积(即683854798468133589409768665696700901
)是具有产品最大功率的除数在给定列表中的所有1000个数字。