如何用一定数量的除数计算最小数?

时间:2015-07-07 13:40:28

标签: algorithm math

来自Project Euler problem 500

  

120的除数是16.实际上,120是具有16个除数的最小数。

     

找到2 ** 500500除数的最小数字。给你的答案模500500507。

它很简单,可以计算n的除数,例如。在Python len([i for i in range(1,n+1) if n % i == 0])中。这是O(n)。

我尝试了强力搜索,发现32个除数的最小数字是840,但对于上面的问题它太慢了。从不平等count_divisors(n) <= n来看,这个数字将是巨大的。

你怎么看?有任何想法吗?如何用一定数量的除数计算最小数?

修改:我不认为这是重复的。这个问题更具体,它涉及特定类别的个更大的数字。 other question一般会问。它的答案并不适用于这个问题,它的大小不同。

9 个答案:

答案 0 :(得分:10)

您应该使用整数n除数的公式:

d(n)=(a 1 +1)(a 2 +1) ... (a <子>ķ 1)

,其中

n = p 1 a 1 * p 2 a 2 * p 3 a 3 * ... * p k < SUP>一个<子>ķ

是通过其素数除数的每个整数的唯一表示。这是一个众所周知的公式,但如果有人想知道如何获得它,请注意dn当且仅当d的形式为p 1 x 1 * p 2 x 2 * p 3 x 3 * ... * p k x k ,其中每个x i 介于0和 i 之间,因此有一个 i + 1种可能性来选择每个x 。现在只需应用产品规则即可获得所需的公式。

对于固定d(n)(如您的情况),n的最小值显然是通过仔细选择现有素数的幂或通过添加新素数来获得的。让我们通过这个简单的例子,16:

d(x)=(a 1 +1)(a 2 +1) ... (a k +1)= 16 = 2 4

这意味着您最多有4个不同的素数,因此:

x = 2 a 1 * 3 a 2 * 5 a 3 * 7 a 4

其中a i &gt; = 0.现在的问题是 - 为了获得x的最小值,增加2的幂是否更好(即增加a 1 ),或者使用7(即取一个 4 = 1而不是 4 = 0)?嗯,检查起来很简单,2 * 3 * 5 * 7&gt; 2 3 * 3 * 5 = 120,这就是120在这种情况下回答的原因。

如何概括这种方法?你应该创建min-heap,你可以放置素数的幂,注意除数的数量达到指定的值。在16的情况下,这个最小堆将包含数字2,3,5,7,2 2 ,3 2 ,2 4 等为什么?因为16 = 2 4 ,所以每个(a i +1)必须除以16,即它必须是2的幂。每次添加新的功率时,它应该以2的幂增加左侧(即变量d(x)),因为您的最终目标是找到具有2 500500 除数的最小数字。堆初始化为第一个k素数(在问题陈述中,k = 500500),并且在每个步骤中,当您从堆中弹出p x 时,p 2x返回,结果乘以p x d(x) = 16 = 2 4

的逐步解决方案
Step    Heap    d(x)    x
==========================
0      2,3,5,7    1     1
1      3,4,5,7    2     2
2      4,5,7,9    4     6
3      5,7,9,16   8     24
4      7,9,16,25  16    120

HTH。

答案 1 :(得分:1)

以下是我的Javascript的高级要点 - 其中factorCount表示除数的数量:

  • 找到factorCount
  • 素数因子分解
  • 生成这些素数因素的每个独特组合
  • 对于每个组合,从原始素数因子数组中提取这些组合值,并向此数组添加一个值,即提取的值乘以。然后按降序对元素进行排序。
  • 对于上一步创建的每个阵列,检查哪个产生最小数时计算2 ^(b1-1)* 3 ^(b2-1) 5 ^(b3- 1) ...
  • 计算出的最小数字是{strong>最小数,var primeFactors = findPrimeFactors(factorCount); var primeFactorCombinations = removeDuplicateArrays(generateCombinations(primeFactors, 1)); var combinedFactorCandidates = generateCombinedFactorCombinations(primeFactors, primeFactorCombinations); var smallestNumberWithFactorCount = determineMinimumCobination(combinedFactorCandidates); 除数

以下是我的JavaScript函数的高级代码细分:

function smallestNumberByFactorCount(factorCount) {

  function isPrime(primeCandidate) {
    var p = 2;
    var top = Math.floor(Math.sqrt(primeCandidate));
    while(p<=top){
      if(primeCandidate%p === 0){ return false; }
      p++;
    }
    return true;
  }

  function findPrimeAfter(currentPrime) {
    var nextPrimeCandidate = currentPrime + 1
    while(true) {
      if(isPrime(nextPrimeCandidate)){
        return nextPrimeCandidate;
      } else {
        nextPrimeCandidate++;
      }
    }
  }

  function findPrimeFactors(factorParent) {
    var primeFactors = [];
    var primeFactorCandidate = 2;
    while(factorParent !== 1){
      while(factorParent % primeFactorCandidate === 0 && factorParent !== 1 ){
        primeFactors.push(primeFactorCandidate);
        factorParent /= primeFactorCandidate;
      }
      primeFactorCandidate = findPrimeAfter(primeFactorCandidate);
    }
    return primeFactors;
  }

  function sortArrayByValue(a,b){
    return a-b;
  }

  function clone3DArray(arrayOfArrays) {
    var cloneArray = arrayOfArrays.map(function(arr) {
      return arr.slice();
    });
    return cloneArray;
  }

  function doesArrayOfArraysContainArray(arrayOfArrays, array){
    var aOA = clone3DArray(arrayOfArrays);
    var a = array.slice(0);
    for(let i=0; i<aOA.length; i++){
      if(aOA[i].sort().join(',') === a.sort().join(',')){
        return true;
      }
    }
    return false;
  }

  function removeDuplicateArrays(combinations) {
    var uniqueCombinations = []
    for(let c=0; c<combinations.length; c++){
      if(!doesArrayOfArraysContainArray(uniqueCombinations, combinations[c])){
        uniqueCombinations[uniqueCombinations.length] = combinations[c];
      }
    }
    return uniqueCombinations;
  }

  function generateCombinations(parentArray, minComboLength) {
    var generate = function(n, src, got, combinations) {
      if(n === 0){
        if(got.length > 0){
          combinations[combinations.length] = got;
        }
        return;
      }
      for (let j=0; j<src.length; j++){
        generate(n - 1, src.slice(j + 1), got.concat([src[j]]), combinations);
      }
      return;
    }
    var combinations = [];
    for(let i=minComboLength; i<parentArray.length; i++){
      generate(i, parentArray, [], combinations);
    }
    combinations.push(parentArray);
    return combinations;
  }

  function generateCombinedFactorCombinations(primeFactors, primeFactorCombinations) {
    var candidates = [];
    for(let p=0; p<primeFactorCombinations.length; p++){
      var product = 1;
      var primeFactorsCopy = primeFactors.slice(0);
      for(let q=0; q<primeFactorCombinations[p].length; q++){
        product *= primeFactorCombinations[p][q];
        primeFactorsCopy.splice(primeFactorsCopy.indexOf(primeFactorCombinations[p][q]), 1);
      }
      primeFactorsCopy.push(product);
      candidates[candidates.length] = primeFactorsCopy.sort(sortArrayByValue).reverse();
    }
    return candidates;
  }

  function determineMinimumCobination (candidates){
    var minimumValue = Infinity;
    var bestFactorCadidate = []
    for(let y=0; y<candidates.length; y++){
      var currentValue = 1;
      var currentPrime = 2;
      for(let z=0; z<combinedFactorCandidates[y].length; z++){
        currentValue *= Math.pow(currentPrime,(combinedFactorCandidates[y][z])-1);
        currentPrime = findPrimeAfter(currentPrime);
      }
      if(currentValue < minimumValue){
        minimumValue = currentValue;
        bestFactorCadidate = combinedFactorCandidates[y];
      }
    }
    return minimumValue;
  }

  var primeFactors = findPrimeFactors(factorCount);
  var primeFactorCombinations = removeDuplicateArrays(generateCombinations(primeFactors, 1));
  var combinedFactorCandidates = generateCombinedFactorCombinations(primeFactors, primeFactorCombinations);
  var smallestNumberWithFactorCount = determineMinimumCobination(combinedFactorCandidates);

  return smallestNumberWithFactorCount;
}

这是完整的sha-bang:

> smallestNumberByFactorCount(3) --> 4
> smallestNumberByFactorCount(4) --> 6
> smallestNumberByFactorCount(5) --> 16
> smallestNumberByFactorCount(6) --> 12
> smallestNumberByFactorCount(16) --> 120
> smallestNumberByFactorCount(100) --> 45360
> smallestNumberByFactorCount(500) --> 62370000
> smallestNumberByFactorCount(5000) --> 4727833110000
> smallestNumberByFactorCount(100000000) --> 1.795646397225103e+40

将上述代码块粘贴到浏览器控制台中,您可以自行测试:

org.elasticsearch.spark

当输入达到1亿左右时,我的算法就开始瘫痪...所以对于Project Euler问题500,输入将是2 ^ 500500(真的,真的,非常大的数字)你需要另一种方法。但是,这是一个很好的通用方法,可以让你走得很远。希望它有所帮助。

请留下评论效率优化建议。我很乐意听到他们。

答案 2 :(得分:0)

除了数字之外,任何数字的最高除数是数字的一半。例如,120除了自身之外还有60的最大除数。因此,您可以轻松地将范围从(n + 1)减小到(n / 2)。

此外,对于具有m个除数的数字,该数字必须至少遵循上述逻辑((m-1)* 2)(-1因为第m个数字本身)。例如,一个带有4个除数的数字必须至少为6.因此,你现在搜索n的范围较小。

这两个会稍微减少运行时间。

答案 3 :(得分:0)

不完全回答一些提示:

  1. n的最大整数除数为n/2

    因此无需检查最多n

  2. 的所有除数
  3. 可以使用素数分解

    最大素数除数为sqrt(n),因此无需测试最多n而是测试最多sqrt(n)或最多为n位的一半

    m=(2^(ceil(ceil(log2(n))/2)+1))-1
    

    应该加快一些事情,但你需要添加非素数除数的计算

  4. 这看起来像某种系列(素数分解)

    divisors  | prime_divisors | non_prime_divisors              | LCM(all divisors)
    1         | 1               |                                 | 1
    2         | 1,2             |                                 | 2
    3         | 1,2             | 4                               | 4
    4         | 1,2,3           | 6                               | 6
    5         | 1,2             | 4,8,16                          | 16
    6         | 1,2,3           | 4,6,12                          | 12
    7         | 1,2             | 4,8,16,32,64                    | 64
    8         | 1,2,3           | 4,6,8,12,24                     | 24
    ...
    16        | 1,2,3,5        |4,6,8,10,12,15,20,24,30,40,60,120 | 120
    

    因此,尝试找到生成此顺序的等式,然后简单地计算模数算术中的n-th次迭代(简单PI操作... modmul)。我可以看到偶数和奇数元素都有单独的等式......

  5. [edit1]分解最多16个除数

      1:    1
      2:    1,   2
      3:    1,   2,   4
      4:    1,   2,   3,   6
      5:    1,   2,   4,   8,  16
      6:    1,   2,   3,   4,   6,  12
      7:    1,   2,   4,   8,  16,  32,  64
      8:    1,   2,   3,   4,   6,   8,  12,  24
      9:    1,   2,   3,   4,   6,   9,  12,  18,  36
     10:    1,   2,   3,   4,   6,   8,  12,  16,  24,  48
     11:    1,   2,   4,   8,  16,  32,  64, 128, 256, 512,1024
     12:    1,   2,   3,   4,   5,   6,  10,  12,  15,  20,  30,  60
     13:    1,   2,   4,   8,  16,  32,  64, 128, 256, 512,1024,2048,4096
     14:    1,   2,   3,   4,   6,   8,  12,  16,  24,  32,  48,  64,  96, 192
     15:    1,   2,   3,   4,   6,   8,   9,  12,  16,  18,  24,  36,  48,  72, 144
     16:    1,   2,   3,   4,   5,   6,   8,  10,  12,  15,  20,  24,  30,  40,  60, 120
    

答案 4 :(得分:0)

正如Miljen Mikic所解释的那样,除数计数功能由素数因子分解决定。要计算n,从1开始并使用贪婪算法将除数的数量加倍k次,在每一步选择最便宜的因子。初始成本是素数,当您使用它们时,它们将替换为正方形。在预先计算了前k个素数后,您可以使用最小堆快速完成此操作。在Python中

import primesieve # pip install primesieve
import heapq

def solve(k, modulus=None):
    """Calculate the smallest number with 2**k divisors."""
    n = 1

    costs = primesieve.generate_n_primes(k) # more than necessary

    for i in range(k):
        cost = heapq.heappop(costs)
        heapq.heappush(costs, cost**2)
        n *= cost
        if modulus:
            n %= modulus

    return n

assert solve(4) == 120

if __name__ == "__main__":
    print(solve(500500, 500500507))

答案 5 :(得分:0)

Phew,我刚刚在java中解决了它。我的最小数字,2 ^ n除数&#34;最终被表现为素数及其权力的地图。

这个谜题与优化一样多:让你的代码工作,然后重构。它绝对值得测试大约2 ^ 30个除数,因为在优化时存在各种二阶错误的空间。重用早期计算的结果,尽量不对任何东西进行排序,并尽快停止迭代(NavigableSet和LinkedHashMap很有用)。我不会教我的祖母有效地测试素性。

我在整个过程中都使用了java,但是在这个大小的数字中,在计算过程中很容易超过Long.MAX_VALUE,请记住:(A ^ 2 * B)%C =(A *( (A * B)%C))%C。

希望所有这些都能激发而不是让游戏消失。继续!

答案 6 :(得分:0)

这是我的代码。您可以使用筛号生成素数。如果您对我的代码有任何建议,请提出建议。

def findfactors(num):
    list1=[]
    for i in range(num,1,-1):
        if num%i==0:
            list1.append(i)
    return list1

def getcombinations(num):
    factors=findfactors(num)
    list1=[]
    if num==1:
        return 1
    for i in factors:
        if getcombinations(num//i)!=1:
            list1.append([i,getcombinations(num//i)])
        else:
            list1.append(i)
    return list1

def unloadlist(list1):
    list2=[]
    if type(list1).__name__=="list":
        for i in list1[1]:
            if type(i).__name__=="list":
                i=unloadlist(i)
            if type(i).__name__=="list":
                flag=True
                for j in i:
                    if type(j).__name__=="list":
                        list2.append([list1[0]]+j)
                        flag=False
                if flag==True:
                    list2.append([list1[0]]+i)
            else:
                list2.append([list1[0],i])
        if len(list2)==1:
            list2=list2[0]
    else:
        list2=list1
    return list2

def mergeitems(list1):
    list2=[]
    for i in list1:
        if type(i).__name__=="int":
            list2.append((i,))
        elif type(i).__name__=="list":
            if type(i[0]).__name__!="list":
                list2.append(tuple(sorted(i,reverse=True)))
            else:
                for j in i:
                    list2.append(tuple(sorted(j,reverse=True)))
    set1=set(list2)
    return set1

def find_smallest_number(num):
    #start writing your code here
    list1=getcombinations(num)
    for i in range(0,len(list1)):
        list1[i]=unloadlist(list1[i])
    mainlist=mergeitems(list1)
    possibles=[]
    primes=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227]
    for i in mainlist:
        num=1
        for j in range(0,len(i)):
            num*=primes[j]**(i[j] - 1)
        possibles.append(num)
    return min(possibles)

num=7
print("The number of divisors :",num)
result=find_smallest_number(num)
print("The smallest number having",num," divisors:",result)

答案 7 :(得分:0)

缩写形式的素因式分解(PF),用于具有 2 ** 500,500除数如下所示:

2**31 * (3...7)**15 * (11...47)**7 * (53...2,713)**3 * (2,719...7,370,029)

具有2 ^ n除数的最小正整数是前n个素数的序列与素数的幂的乘积。素数(pp)的幂是提高到2 ^(2 ^ e)幂的素数(幂是2,4,8,16 ...)。前3个pp是4,9和16(2 ^ 2,3 ^ 2和2 ^ 4)。

由于PF中仅使用素数,因此您会注意到pp与素数合计。对于n = 500,550,这是一些统计或计数: PF中的5个词(幂31,15,7,3,1)。 总素数(P)= 500,084 素数的幂(pp)= 416 总P + pp = n = 500,500参见上面的定理。

Single primes (the last term of the PF) total 499,688.

您可以使用Sage Math和Excel计算此PF(以及2 ^ n的其他PF)(尽管使用Excel,您需要素数计算功能)。

此外,您还应该检查PF解决方案。通过将权重分配给各个pp和单质数以检查原始n,可以通过几种方式完成。

答案 8 :(得分:-2)

功能齐全的代码。

def find_smallest_number(num):
    number2=0

    if(num%8==0):
        number2=min(2**((num/4)-1)*3**1*5**1 , 2**((num//2)-1)*3**1)
    elif(num%9==0):
        number2=2**((num/9)-1)*3**2*5**2   
    elif(num%2==0 and num%3==0):
        number2=2**((num/6)-1)*3**2*5**1   
    elif((num%4==0)):
        number2=2**((num/4)-1)*3**1*5**1
    elif(num%2==0):
        number2=2**((num/2)-1)*3**1
    else:
        number2=2**(num-1)         

    return number2   


num=32
print("The number of divisors :",num)
result=find_smallest_number(num)
print("The smallest number having",num," divisors:",result)