【问题标题】:Find the largest divisor of N that is less than sqrt(N)找到小于 sqrt(N) 的 N 的最大除数
【发布时间】:2012-01-06 13:23:43
【问题描述】:

实际上,给定 N a(可能非常大)偶数,我想找到 N = F * R 其中 gcd(F,R) = 1,F>R,并且 F 尽可能小(因为 I'将完全考虑 F)。问题的核心是找到最大除数 R,其中 R

例如,N=36 应该给出 F=9 和 R=4。请注意,R 不一定是素数或素数幂。请注意,我没有考虑 N。对 F 和 R 的唯一限制是它们是互质的。

这是我的快速而幼稚的版本,它正在工作:

def factor_partial(N):
    for R in xrange(int(math.sqrt(N)),1,-1):
        if N%R == 0 and gcd(R,N/R) == 1:
            return N/R, R

我想象的另一种方法是按递增顺序查找除数,并在此过程中删除任何非除数的倍数。比如:

def factor_partial(N):
    i = range(2, int(sqrt(N)) + 1)
    while i:
        if N % i[0] != 0:
            remove_multiples(i, i[0]) #without removing i[0]
        else:
            if gcd(i[0], N/i[0]) == 1:
                R = i[0]
        i.pop(0) #remove i[0]

    return N/R, R

我认为这会很慢并且会占用大量内存,但如果i 是一个生成器,它可能会很高效。我没怎么用过生成器。

我可以改进第一个版本吗?第二个版本可行(我该怎么做)?有没有更好的完全不同的方法?

在 python、c 或伪代码中寻找答案。


这是一个数论课程的项目。我正在实施基于Pocklington 的素数测试。虽然我需要一个分解算法,但我们还没有研究过任何算法,而且我可能不会使用诸如二次筛之类的算法,这超出了我的课程范围。我正在就所提出的问题寻求具体帮助。

【问题讨论】:

  • F * R == N 吗?我不确定所有这些变量的含义
  • @Blender 是的,谢谢,已更新。
  • 您的第二种方法是Sieve of Eratosthenes。我建议你也看看this question
  • @Blender 是的,在某种程度上。我为经典筛子编写了代码来生成素数,但它也不使用生成器并且内存不足。也许是时候升级那个了。
  • 筛子不适用于大量数字,因为您必须在内存中的某个时间点存储每个前面的数字,这会以非常快的速度消耗 RAM。这只是运行筛子所需存储的数字数量(n 是数字数量):n(n+1)/2

标签: python c math generator factorization


【解决方案1】:

维基百科有一个很好的分解算法列表:http://en.wikipedia.org/wiki/Integer_factorization#Factoring_algorithms

您的第二种方法有效地使用了筛子,并且当 N 是某个小素数的倍数时,它具有快速减少问题的良好特性。可以通过循环遍历素数而不是 2..sqrt(n) 的所有可能除数来改进代码。

此外,您可能希望从素数测试开始,以便在进行其他工作之前知道 N 是合数。

您的注释说您没有考虑 N,但问题是相关的。寻找FR等于探索N的素因数的非重叠组合。

N==36 的情况下,N 的素数分解为2, 2, 3, 3。 F 和 R 的因子必须包括所有这些(这样F*R==N)并且不能有重叠(这样GCD(F,R)==1)。所以 4 和 9 立即出现。

一个更有启发性的例子可能是N==23256。它的因式分解为2,2,2,3,3,17,19。由于 FR 之间不能有重叠,因此每个素数碱基只能进入两个桶中的一个(即你要么得到所有的两个,要么一个都没有) .因此,我们可以将这些因素分组为8,9,17,19。要找到 R,我们需要尽可能大但低于 152.49(23256 的平方根)的那些因子的组合。我们的选择是 {8}、{9}、{8,9}、{8,17} , {8,19}。其中最大的是8*19,即152。对应的F17*19或153。

上面列出的选项计算为[choice for choice in powerset([8,9,17,19]) if prod(choice) < math.sqrt(N)]

所以整个程序几乎可以归结为:

prime_factors = factorize(N)      # [2,2,2,3,3,17,19]
clusters = [p**e for p, e in collections.Counter(prime_factors).items()]  # [8,9,17,19]
R = max(prod(group) for group in powerset(clusters) if prod(group) < math.sqrt(N))
F = N // R

powerset 搜索可以通过在集合的生成超过 N 上的平方根时对其进行修剪来加快。

请记住,因式分解在计算上是昂贵的,并且幂集增长非常快,但它可能比从 N 的平方根开始进行许多除法并向下工作的原始算法的工作量要少得多.

【讨论】:

  • 假设 N 是 36,那么我正在寻找的解决方案是 R=4,F=9。如果我循环遍历素数,我找不到 R=4。如果我们限制只检查素数,我们如何恢复这个解决方案?
  • "4" 甚至不应该在数字分解中列出 - 你需要找到的是主要因素。在这种情况下,你应该找到两次“2”,
  • @jsbueno 不。我没有考虑 N。对 F 和 R 的唯一限制是它们是相对质数。
  • @jmilloy:关键是解决您的问题的最简单方法是首先考虑因素 N,然后对因素进行分组。
  • 谢谢。这有点可笑 - 我正在编写一个素性测试,关键是应该更容易找到 N-1 = F*R 和 F 因子而不是 N 因子。决定尝试最小化 F,结果证明(似乎)与首先分解 N-1 一样困难。
【解决方案2】:

你能否得到 N 的素因数分解,然后找到小于 sqrt(N) 的所有素因数的最大乘积组合?

例如对于 36,它会发现素数分解是 2*2*3*3。然后你会尝试所有不同的素数组合:

2 = 2
3 = 3
2*2 = 4
2*3 = 6
3*3 = 9
2*2*3 = 12
2*3*3 = 18
2*2*3*3 = 36

你知道这些都是 36 的因数,所以你找到最大的一个,它小于 sqrt(36),结果是 4。

但是,除非您已经拥有现有的素数或素因数分解列表,或者一些很棒的素数分解算法,否则我真的不认为这会比执行您的第一个版本快得多,或者您正在用非常大的数字做所有这些。

但即便如此(回到第一个版本)O(sqrt(n)) 是一个非常快的运行时,并且只需要 O(1) 内存,所以实际上第一个算法可能只是要走的路。我看不出它有多慢,尤其是在现代计算机上的 C 语言中。

【讨论】:

  • 嗯,是的,对 N 进行因式分解可以解决 整个 问题...但是该算法代替了对 N 的因式分解:P
  • 哦哈哈。那么我会坚持你的第一个版本。它在内存和运行时都非常有效。祝你好运!
  • 没有必要考虑像 R=2*3 这样的组合,因为这样 F 会得到其他素数 F=2*3 并且那些不会是相对素数。所以你要么在给定的因素中得到所有的两个,要么一个都没有。因此,可能的候选因子是 {1, 4, 9, 36}。
  • 恐怕我不太听从你的解释?
  • OP 想要两个因子 F 和 R 乘以 N,R 尽可能大(直到 sqrt(N),并且彼此互质。后一个限制意味着 2 *3 不是 R 的候选者(因为相应的 F=N/R 不会与 R 互质)。此限制有助于大幅修剪搜索空间。
【解决方案3】:
def factor_partial(N):
    R = int(sqrt(float(N)))
    sieve = [1, 1] + [0] * (R-1)
    for i in xrange(2, R) :
        if sieve[i]==0 :
            j=i*i;
            while j<=R :
                sieve[j]=1
                j = j + i
    primes = [i for i in xrange(R+1) if sieve[i]==0]

    saveN = N
    primepower_divisors = []
    for i in primes :
        if N%i == 0 :
            term = 1
            while N%i == 0 :
                term = term * i
                N = N / i
            primepower_divisors = primepower_divisors + [term]
            if N==1 : break

    largecoprime_divisors = [1]
    for i in primepower_divisors :
        for j in largecoprime_divisors :
            largecoprime_divisors = largecoprime_divisors + [i*j]

    F = min([i for i in largecoprime_divisors if i>R])
    return F, saveN/F

我已经使用筛法计算素数列表(计算素数列表有很多优化可能) 我们可以使用这样一个事实,即 .. 没有素数 p 使得 F%p == 0 和 R%p == 0 。 由于 gcd(F, R)=1

【讨论】:

  • 通常 min([i for i in largecoprime_divisors if i>R]) 失败,因为它有一个空序列。其他时候它比我的第一个版本慢。
  • 是的,这个比较慢 .. 我刚刚发现 .. :) 也许你应该使用 Polard 的 Rho ..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-03
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多