【问题标题】:Find the number in the range 1 to 100 that has the most divisors [closed]找到 1 到 100 范围内除数最多的数字 [关闭]
【发布时间】:2012-11-22 10:19:08
【问题描述】:

如何找到 1 到 100 范围内除数最多的最小数? 我知道一个简单的方法是检查从 1 到 100 的每个数字的除数,并跟踪具有最大除数的数字。 但是有没有更有效的方法呢?

【问题讨论】:

  • 对于 100,您的方法很好,O(n^2) 对于这么小的输入应该不是什么大问题。
  • 如果范围非常大到数百万怎么办?
  • 那是不同的问题,应该区别对待。是这样吗?如果是这样,请编辑您的问题以表明它。
  • 从 2 开始,不断乘以素数,直到达到 100。

标签: c algorithm numbers


【解决方案1】:

对于从 1 到 100 的每个数字,您可以检查它的所有倍数并添加除数。根据您检查每个数字的除数的方式,它可能会更有效。这是一个实现这个想法的python代码。复杂度是 O(N log N)

count=[0]*101
for i in xrange(1,101):
  for j in xrange(1,100/i+1):
    count[i*j]+=1

print max(zip(count,xrange(101))) 

这是C语言的代码

int i,j,count[101];
for(i=1;i<=100;i++) for(j=1;j<=100/i;j++) count[i*j]++;
int max=-1,pos;
for(i=1;i<=100;i++) if(count[i]>=max){
   max=count[i];
   pos=i;
}
printf("%d has %d divisors\n",pos,max);

两个版本都保留了具有最大除数的所有数字中的最大数字。在这种情况下,96 有 12 个除数。

【讨论】:

  • 我无法将 cmets 添加到其他已发布的答案中,但想提一下此解决方案计算除数的数量,而其他解决方案计算主要因素的数量。虽然它们是相关的,但我看不出它们是如何在不知道每个素因子的幂的情况下计算除数的(你需要这个来计算除数的数量。)
  • 如果您选中if (count[i] &gt;= max),C 代码的输出将与 Python 的输出匹配。我更希望得到具有最大除数的所有数字,但这当然要复杂一些。除非您吸引了一些反对票,否则您将来将拥有comment 特权。
  • 你确定复杂度是 nlogn 因为我没有看到它。它实际上是 n^2
  • 是的,复杂度是 O(N log N)。想想你有多少次进入内在循环。当 i=1 时,您执行 n 步 j,当 i=2 时,您执行 N/2 步 j,当 i=3 时,您执行 N/3 步。从 i=j/2 到 j 你只做 j 的 1 步。所以你有从 1 到 N 的总和(N/i)。这已知是 O(N log N),如果您不相信,可以编写一个简单的检查器。
  • @bcurcio 如果你写 xrange(1, 100//i + 1)for(j = 1; j &lt;= 100/i; ++j) 可能会更明显。
【解决方案2】:

您可以从 Eratosthenes 算法的筛子中获得一些想法。唯一的问题是您需要从 2*i 而不是 i*i 运行内部循环。但是这个算法比 O(n^2) 快

 int a[]=new int[101],max=0,index=-1;
for(i=2;i<=100;i++)
{
if(a[i]==0)
for(j=2*i;j<=100;j+=i)
a[j]++;
if(a[i]>max)
{
index=i;
max=a[i];
}

这为您提供 30,除数为 3。如果您想要答案中的变体,您可以修改内部循环

【讨论】:

    【解决方案3】:

    一种方法是避免奇数..

    int mostDivisors(int min,int max)
    {
        int i,j,pc=0,cc=0,no=0;
        min=(min%2==0)?min:min+1;//making it even
    
        for(i=min;i<=max;i+=2)//checking only even numbers
        {
            cc=0;
            for(j=2;j<i;j++)//avoiding dividing by 1 and itself
            {
                if(i%j==0)cc++;
            }
            if(pc<cc)
            {
                 no=i;
                 pc=cc;
            }
        }
        return no;
    }
    

    【讨论】:

      【解决方案4】:

      有一种“更简单的方法”,但它是理论上的,而不是真正的计算机算法。出现了两种不同的情况 - 一种是您所说的“大多数因素”就是这个意思,另一种是因素必须是唯一的。

      在第一种情况下,您只需要认识到,要最大化因子的数量,每个因子需要尽可能小,即 2。小于 100 的数字具有最多的因子,因此是最大的2 的幂小于 100,恰好是 64。

      如果因子必须是唯一的,那么我们只需使用 2、3、5 等(质数),直到下一个累积乘积大于 100 - 在这种情况下 2*3*5=30 是数字具有最独特的因素。添加第四个因素会使它变成 210,所以这是我们可以达到的最高值。

      【讨论】:

      • std::uint8_t get_integer_with_most_divisors_between_1_and_100() { return 42; }。啊等等,那应该是64(或者30,如果需要唯一性的话)。
      • 这是错误的。 64 = 2^6 有 7 个除数,但 60 = 2^2 * 3 * 572 = 2^3 * 3^296 = 2^5 * 3 都有 12 个除数。
      • 唯一因素是必需的。
      • 但不会计算素数本身需要一些计算。
      • @DanielFischer 好点,如果 OP 实际上意味着除数(诚然,这是最初使用的词)。大多数其他答案/cmets 似乎将这个问题解释为询问因素,所以我做了同样的假设,但实际上使用了因素这个词。如果实际上是指除数,那将是一个截然不同(也更难)的问题。
      【解决方案5】:

      对于较小的界限,使用筛子就足够了。实事求是

               r                   r
      (1)  n = ∏ p_k^e_k => τ(n) = ∏ (e_k + 1)
              k=1                 k=1
      

      很明显,除数的数量可以很容易地从n 的素因数分解中确定,如果gcd(m,n) = 1τ(m*n) = τ(m) * τ(n)(即τ 是一个乘法函数)。

      因此,如果我们知道n 的任何素因数以及1 &lt;= m &lt; n 的所有τ(m),我们就可以廉价地计算τ(n)。因此

      int sieve[limit+1];
      // initialise sieve
      for(int i = 0; i <= limit; ++i) {
          sieve[i] = i;
      }
      // find a prime factor for all numbers > 1
      int root = sqrt(limit); // limit is supposed to be not too large, so no fixup needed here
      for(int p = 2; p <= root; ++p) {
          if (sieve[p] == p) {
              // this means p is prime, mark multiples
              for(int m = p*p; m <= limit; m += p) {
                  sieve[m] = p;
              }
      }
      // Now sieve[n] is a prime factor of n
      int p;
      for(int n = 2; n <= limit; ++n) {
          if ((p = sieve[n]) == n) {
              // a prime, two divisors
              sieve[n] = 2;
          } else {
              // count the multiplicity of p in n and find the cofactor of p^multiplicity
              int m = 1, q = n;
              do {
                  q /= p;
                  ++m;
              }while(q % p == 0);
              sieve[n] = m*sieve[q];
          }
      }
      // Now sieve[n] contains τ(n), the number of divisors of n, look for the maximum
      int max_div = 0, max_num = 0;
      for(int n = 1; n <= limit; ++n) {
          if (sieve[n] > max_div) {
              max_div = sieve[n];
              max_num = n;
          }
      }
      

      O(N*log log N) 时间内找到最大除数不超过N 的最小数,具有相对较小的常数因子(可以通过分别处理 2 并仅标记奇质数的奇数倍来进一步减小)。

      这是一个简单的蛮力方法,对于小N 来说足够快(“小”的解释取决于“足够快”的概念,例如可以是&lt;= 1000&lt;= 1000000)。

      对于更大的范围,这太慢且太占用内存。对于这些,我们需要做更多的分析。

      从 (1) 中,我们可以推断出在所有具有相同质因数分解结构的数中(意味着相同的数 r 的不同质因数,以及相同的多重指数集,但可能以不同的顺序),都具有相同数量的除数,最小的那个是

      • 素数是r 最小的素数
      • 指数按降序显示(2 的指数最大,3 次大,...)

      所以我们可以通过考虑所有有限序列来找到具有最多除数&lt;= N 的最小数

      e_1 >= e_2 >= ... >= e_r > 0
      

      属性

                               r
      N/2 < n(e_1, ..., e_r) = ∏ p_k^e_k <= N
                              k=1
      

      并且寻找的号码是他们生产的n(e_1, ..., e_r)之一。 (如果n(e_i) &lt;= N/2 是一个单调非递增有限序列,则在e_1 上加1 的序列将产生一个具有更多除数的数&lt;= N。)

      为与1/log p_k 大致成比例的指数生成最大除数。更准确地说,对于固定的r,让

                         r
      T(x_1, ..., x_r) = ∏ (x_k+1)
                        k=1
      
                         r
      F(x_1, ..., x_r) = ∏ p_k^x_k
                        k=1
      

      然后T 在集合{ x : F(x) = N and x_k &gt; 0 for all k } 上取其最大值

                     r
      x_k = (log N + ∑ log p_k)/(r * log p_k) - 1
                    k=1
      

      我们只承认整数指数,这会使问题复杂化,但偏离比例太远会产生除数比接近比例时要少的数字。

      让我们为N = 100000 说明它(它有点太小,无法真正利用比例性,但足够小,完全可以手动完成):

      1. r = 1:e_1 = 16,n(16) = 2^16 = 65536有17个除数。

      2. r = 2:设置x_2 = x_1 * log 2 / log 3N = 2^x_1 * 3^x_2 = 2^(2*x_1),我们得到x_1 ≈ 8.3, x_2 ≈ 5.24。现在让我们看看e_1, e_2 接近x_1, x_2 会发生什么。

        2^7 *3^6 = 93312, τ(2^7 *3^6) =  (7+1)*(6+1) = 56
        2^8 *3^5 = 62208, τ(2^8 *3^5) =  (8+1)*(5+1) = 54
        2^10*3^4 = 82944, τ(2^10*3^4) = (10+1)*(4+1) = 55
        

        远离比例关系会迅速减少除数,

        2^11*3^3 = 55296, τ(2^11*3^3) = (11+1)*(3+1) = 48
        2^13*3^2 = 73728, τ(2^13*3^2) = (13+1)*(2+1) = 42
        2^15*3^1 = 98304, τ(2^15*3^1) = (15+1)*(1+1) = 32
        

        因此,最接近比例的一对并没有产生最大的除数,但具有大除数的一对是最接近的三个。

      3. r = 3:同理,我们得到x_1 ≈ 5.5, x_2 ≈ 3.5, x_3 ≈ 2.4

        2^4 *3^3*5^3 = 54000, τ(2^4 *3^3*5^3) =  5*4*4 = 80
        2^5 *3^4*5^2 = 64800, τ(2^5 *3^4*5^2) =  6*5*3 = 90
        2^7 *3^3*5^2 = 86400, τ(2^7 *3^3*5^2) =  8*4*3 = 96
        2^8 *3^2*5^2 = 57600, τ(2^8 *3^2*5^2) =  9*3*3 = 81
        2^6 *3^5*5^1 = 77760, τ(2^6 *3^5*5^1) =  7*6*2 = 84
        2^7 *3^4*5^1 = 51840, τ(2^7 *3^4*5^1) =  8*5*2 = 80
        2^9 *3^3*5^1 = 69120, τ(2^9 *3^3*5^1) = 10*4*2 = 80
        2^11*3^2*5^1 = 92160, τ(2^11*3^2*5^1) = 12*3*2 = 72
        2^12*3^1*5^1 = 61440, τ(2^12*3^1*5^1) = 13*2*2 = 52
        

        同样,对于接近比例的指数,可以实现较大的除数。

      4. r = 4:指数的粗略近似是x_1 ≈ 4.15, x_2 ≈ 2.42, x_3 ≈ 1.79, x_4 ≈ 1.48。对于e_4 = 2,只有一种选择,

        2^3*3^2*5^2*7^2 = 88200, τ(2^3*3^2*5^2*7^2) = 4*3*3*3 = 108
        

        对于e_4 = 1,我们有更多选择:

        2^4*3^3*5^2*7^1 = 75600, τ(2^4*3^3*5^2*7^1) =  5*4*3*2 = 120
        2^5*3^2*5^2*7^1 = 50400, τ(2^5*3^2*5^2*7^1) =  6*3*3*2 = 108
        2^5*3^4*5^1*7^1 = 90720, τ(2^5*3^4*5^1*7^1) =  6*5*2*2 = 120
        2^6*3^3*5^1*7^1 = 60480, τ(2^6*3^3*5^1*7^1) =  7*4*2*2 = 112
        2^8*3^2*5^1*7^1 = 80640, τ(2^8*3^2*5^1*7^1) =  9*3*2*2 = 108
        2^9*3^1*5^1*7^1 = 53760, τ(2^9*3^1*5^1*7^1) = 10*2*2*2 =  80
        
      5. r = 5x_1 ≈ 3.3, x_2 ≈ 2.1, x_3 ≈ 1.43, x_4 ≈ 1.18, x_5 ≈ 0.96。由于2*3*5*7*11 = 2310,7和11的指数一定是1,所以我们找到了候选

        2^2*3^2*5^2*7*11 = 69300, τ(2^2*3^2*5^2*7*11) = 3*3*3*2*2 = 108
        2^3*3^3*5^1*7*11 = 83160, τ(2^3*3^3*5^1*7*11) = 4*4*2*2*2 = 128
        2^4*3^2*5^1*7*11 = 55440, τ(2^4*3^2*5^1*7*11) = 5*3*2*2*2 = 120
        2^6*3^1*5^1*7*11 = 73920, τ(2^6*3^1*5^1*7*11) = 7*2*2*2*2 = 112
        
      6. r = 6:由于2*3*5*7*11*13 = 30030,这里只有一个候选人,

        2^2*3*5*7*11*13 = 60060, τ(60060) = 3*2^5 = 96
        

        这会产生比使用四个或五个素数的最佳候选者更小的除数。

      因此,我们调查了 28 个候选者(并且可以跳过其中几个),发现具有最多除数的最小数字 &lt;= 100000 是 83160(98280 是另一个低于 100000 的数字,具有 128 个除数)。

      这是一个程序,它几乎可以立即找到具有最多除数且不超过给定限制&lt; 2^64 的最小数字(没有尝试过捷径,因为它对于 64 位整数来说足够快,可以实现任意精度整数,这在某些时候会变得有价值):

      #include <stdlib.h>
      #include <stdio.h>
      
      typedef struct {
          unsigned long long number;
          unsigned long long divisors;
      } small_max;
      
      static const unsigned long long primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
      static const unsigned long long primorials[] =
          { 2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870, 6469693230,
            200560490130, 7420738134810, 304250263527210, 13082761331670030,
            614889782588491410 };
      
      static const unsigned num_primes = sizeof primorials / sizeof primorials[0];
      
      small_max max_divisors(unsigned long long limit);
      small_max best_with(unsigned long long limit, unsigned index, unsigned multiplicity);
      void factor(unsigned long long number);
      
      int main(int argc, char *argv[]) {
          unsigned long long limit;
          limit = argc > 1 ? strtoull(argv[1],NULL,0) : 100000;
          small_max best = max_divisors(limit);
          printf("\nSmallest number not exceeding %llu with most divisors:\n",limit);
          printf("%llu with %llu divisors\n", best.number, best.divisors);
          factor(best.number);
          return 0;
      }
      
      small_max max_divisors(unsigned long long limit) {
          small_max result;
          if (limit < 3) {
              result.number = limit;
              result.divisors = limit;
              return result;
          }
          unsigned idx = num_primes;
          small_max best = best_with(limit,0,1);
          printf("Largest power of 2: %llu = 2^%llu\n", best.number, best.divisors-1);
          for(idx = 1; idx < num_primes && primorials[idx] <= limit; ++idx) {
              printf("Using primes to %llu:\n", primes[idx]);
              unsigned long long test = limit, remaining = limit;
              unsigned multiplicity = 0;
              do {
                  ++multiplicity;
                  test /= primorials[idx];
                  remaining /= primes[idx];
                  result = best_with(remaining, idx-1, multiplicity);
                  for(unsigned i = 0; i < multiplicity; ++i) {
                      result.number *= primes[idx];
                  }
                  result.divisors *= multiplicity + 1;
                  if (result.divisors > best.divisors) {
                      printf("New largest divisor count: %llu for\n  ", result.divisors); 
                      factor(result.number);
                      best = result;
                  } else if (result.divisors == best.divisors && result.number < best.number) {
                      printf("Smaller number with %llu divisors:\n  ", result.divisors); 
                      factor(result.number);
                      best = result;
                  }
              }while(test >= primorials[idx]);
          }
          return best;
      }
      
      small_max best_with(unsigned long long limit, unsigned index, unsigned multiplicity) {
          small_max result = {1, 1};
          if (index == 0) {
              while(limit > 1) {
                  result.number *= 2;
                  ++result.divisors;
                  limit /= 2;
              }
              return result;
          }
          small_max best = {0,0};
          unsigned long long test = limit, remaining = limit;
          --multiplicity;
          for(unsigned i = 0; i < multiplicity; ++i) {
              test /= primorials[index];
              remaining /= primes[index];
          }
          do {
              ++multiplicity;
              test /= primorials[index];
              remaining /= primes[index];
              result = best_with(remaining, index-1, multiplicity);
              for(unsigned i = 0; i < multiplicity; ++i) {
                  result.number *= primes[index];
              }
              result.divisors *= multiplicity + 1;
              if (result.divisors > best.divisors) {
                  best = result;
              } else if (result.divisors == best.divisors && result.number < best.number) {
                  best = result;
              }
          }while(test >= primorials[index]);
          return best;
      }
      
      void factor(unsigned long long number) {
          unsigned long long num = number;
          unsigned idx, mult;
          printf("%llu =", number);
          for(idx = 0; num > 1 && idx < num_primes; ++idx) {
              mult = 0;
              while(num % primes[idx] == 0) {
                  num /= primes[idx];
                  ++mult;
              }
              printf("%s %llu ^ %u", idx ? " *" : "", primes[idx], mult);
          }
          printf("\n");
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-23
        • 2021-08-14
        • 2012-10-01
        • 1970-01-01
        • 2018-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多