除数算法

时间:2016-10-30 16:32:12

标签: c++ algorithm primes factorization

我得到一个整数列表(最多1000个),它乘以给定的整数n

我需要在整数n的所有除数中找到最高的幂。

例如:4,7,8乘以224,最高功率则为5,因为224 = 2 ^ 2 * 7 * 2 ^ 3 = 2 ^ 5 * 7.

问题是,1000个整数可能大到2 ^ 64,因此n非常大。

解决此问题的优秀算法是什么?

2 个答案:

答案 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个数字。