【问题标题】:Algorithm to find all the exact divisors of a given integer找到给定整数的所有精确除数的算法
【发布时间】:2012-07-26 19:29:20
【问题描述】:

我想找到一个数字的所有精确除数。 目前我有这个:

{
   int n;
   int i=2;
   scanf("%d",&n);
   while(i<=n/2)
    {
        if(n%i==0)
                  printf("%d,",i);
        i++;
     }
   getch();
}

有什么办法可以改善吗?

【问题讨论】:

    标签: c algorithm numbers performance factors


    【解决方案1】:

    首先,您的代码应具有i &lt;= n/2 的条件,否则可能会遗漏其中一个因素,例如如果n=12,则不会打印6。

    将循环运行到数字的平方根(即i &lt;= sqrt(n))并打印in/i(两者都是n 的倍数)。

    {
       int n;
       int i=2;
       scanf("%d",&n);
       while(i <= sqrt(n))
        {
            if(n%i==0) {
                printf("%d,",i);
                if (i != (n / i)) {
                    printf("%d,",n/i);
                }
            } 
    
            i++;
        }
       getch();
    }
    

    注意:

    • 对于一个完美的平方,平方根不会被打印两次,在循环结束时对@chepner 建议的i*i == n 进行额外的检查。
    • 如果您希望所有因子按升序将值存储在数组中,则在循环结束时对所有数字进行排序并显示。

    【讨论】:

    • 您可以将printf("%d,", i);printf("%d,", n/i); 更改为printf("%d,", i); if(i != n/i){printf("%d,", n/i);}。对于完美的正方形,它不会打印两次根。
    • 只有循环 sqrt(n) 次才能找到所有除数的证据是什么?请解释一下。
    • 怎么样?它会打印 2 和 4。您是指不会打印 1 和 8 的事实吗?
    • @Rndm - 请注意 i &lt;= sqrt(n) 将 i 提升为 double。您可以使用i &lt;= (int)sqrt(n) 来避免这种情况。将if (i != (n / i)) 更改为if (i*i != n) 会更快。
    【解决方案2】:

    通过在 C 中使用“查找所有素因数”来查找所有除数(更快) 最多 18 位。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    unsigned int FindDivisors(unsigned long long divisors[], unsigned long long N) {
        unsigned int lastdiv = 0;
        divisors[lastdiv++] = 1;
        unsigned long long powerfactor = 1;
        unsigned long long number = N;
        while ((number & 1) == 0) {
            powerfactor <<= 1;
            divisors[lastdiv++] = powerfactor;
            number >>= 1;
        }
    
        unsigned long long factor = 3; unsigned long long upto = lastdiv;
        powerfactor = 1;
        while (factor * factor <= number) {
            if (number % factor == 0) {
                powerfactor *= factor;
                for (unsigned int i = 0; i < upto; i++)
                    divisors[lastdiv++] = divisors[i] * powerfactor;
                number /= factor;
            }
            else {
                factor += 2; upto = lastdiv;
                powerfactor = 1;
            }
        }
    
        if (number > 1) {
            if (number != factor) {
                upto = lastdiv;
                powerfactor = 1;
            }
            powerfactor *= number;
            for (unsigned int i = 0; i < upto; i++)
                divisors[lastdiv++] = divisors[i] * powerfactor;
        }
        return lastdiv;
    }
    
    int cmp(const void *a, const void *b) {
        if( *(long long*)a-*(long long*)b < 0 ) return -1;
        if( *(long long*)a-*(long long*)b > 0 ) return 1;
        return 0;
    }
    
    int main(int argc, char *argv[]) {
        unsigned long long N = 2;
        unsigned int Ndigit = 1;
        if (argc > 1) {
            N = strtoull(argv[1], NULL, 10);
            Ndigit = strlen(argv[1]);
        }
        unsigned int maxdiv[] = {1, 4, 12, 32, 64, 128, 240, 448, 768, 1344,
                                 2304, 4032, 6720, 10752, 17280, 26880, 41472, 64512, 103680};
    
        unsigned long long divisors[maxdiv[Ndigit]];
        unsigned int size = FindDivisors(divisors, N);
        printf("Number of divisors = %u\n", size);
    
        qsort(divisors, size, sizeof(unsigned long long), cmp);
        for (unsigned int i = 0; i < size; i++)
            printf("%llu ", divisors[i]);
        printf("\n");
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      可以通过首先排除所有 2 的因数来改进简单的线性搜索。这可以通过简单的位移来完成,或者用一个很好的内在函数计算训练零。这在任何一种情况下都非常快。然后运行 ​​shg 建议的算法(由于不存在 2 的幂,它将运行得更快),并将结果与​​所有可能的 2 的幂结合起来(不要忘记这一步)。这对于具有大量训练零的输入有很大帮助,但如果它们没有,它甚至会有所帮助 - 您不必再测试任何偶数除数,因此循环长度减半。

      剔除一些恒定的低因子(但大于 2)也有帮助。带有常量的模几乎肯定会被编译器优化(或者如果没有,你可以自己做),但更重要的是,这意味着要测试的除数更少。不要忘记将该因素与您找到的除数结合起来。

      您还可以完全分解数字(使用您最喜欢的算法 - 可能 Pollard 的 Rho 会是最好的),然后打印因子的所有产品(除了空产品和完整产品)。对于更大的输入,这很有可能最终更快 - 与简单的线性搜索相比,Pollard 的 Rho 算法可以非常快速地找到因子,因子通常比适当的除数少,最后一步(枚举产品)只涉及快速数学(没有划分)。这对因子非常小的数字很有帮助,Rho 发现的速度最快。

      【讨论】:

      • 你能举个例子来说明如何通过位移来去除2的所有因素吗?位操作更快,所以肯定会提高性能。
      • @jairaj 说你取 24,平方根几乎是 5 但不完全,所以你必须测试除数 2、3 和 4(它们将分别生成 12、8 和 6 )。如果你去掉 2 的所有幂,你就剩下 3。只有 3。3 的平方根小于 2,所以你不需要测试 任何 除数。因此,您从 3 个慢速除法变为零除法 + 几个班次(首先删除 2 的幂,然后将它们重新添加)。明显的胜利。
      • @jairaj 至于合并:您将拥有 2 的 3 个幂:2、4 和 8。这些都是除数。现在将它们中的每一个乘以 3(“所有”其他除数,其中恰好只有一个),得到 6、12 和 24。然后丢弃 24,因为它不是正确的除数。
      【解决方案4】:

      当给定的数字是奇数时,我们甚至可以跳过偶数。 在接受的代码中稍作即兴:)

      这里是查找给定数的因子的 Java 代码。

      import java.util.Scanner;
      public class Factors {
      
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              int t=scanner.nextInt();
              while(t-- > 0) {
                  int n = scanner.nextInt();
                  if(n % 2 == 0) {
                      for(int i = 1; i <= Math.sqrt(n); i++) {
                          if(n % i == 0) {
                              System.out.println(i + ", ");
                              if(i != n/i) {
                                  System.out.println(n/i + ", ");
                              }
                          }
                      }
                  }
                  else {
                      for(int i = 1; i <= Math.sqrt(n); i=i+2) {
                          if(n % i == 0) {
                              System.out.println(i + ", ");
                              if(i != n/i) {
                                  System.out.println(n/i + ", ");
                              }
                          }
                      }
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        这是我的新 C# 版本。感谢 Rndm,它比我第一次尝试快了近 50 倍。

        public static long GetDivisors(long number)
            {
                long divisors = 0;
        
                long boundary = (long)Math.Sqrt(number);
        
                for (int i = 1; i <= boundary; i++)
                {
                    if (number % i == 0)
                    {
                        divisors++;
                        if(i != (number / i))
                        {
                            if (i * i != number)
                            {
                                divisors++;
                            }
                        }
                    }
                }
        
                return divisors;
            }
        

        【讨论】:

          【解决方案6】:

          其中一个答案中的代码有一个乍一看很难发现的错误。如果 sqrt(n) 是一个有效的除数;但n不是一个完全平方数,则省略两个结果。

          例如试试n = 15,看看会发生什么; sqrt(15) = 3,所以while循环的最后一个值是2。下一条执行if (i * i == n)的语句将作为if(3 * 3 == 15)执行。所以 3 没有被列为除数,5 也被遗漏了。

          以下将正确处理正整数的一般情况。

           {
             int n;
             int i=2;
             scanf("%d",&n);
             while(i <= sqrt(n))
              {
                  if(n%i==0) {
                      printf("%d,",i);
                      if (i != (n / i)) {
                          printf("%d,",n/i);
                      }
                  } 
          
                  i++;
              }
             getch();
          }
          

          【讨论】:

            【解决方案7】:
              int count = 2;
                 //long childsum = 0;
                       long _originalvalue = sum;
                 dividend = "1";
                 for (int i = 2; i < sum; i++)
                 {
                     if (_originalvalue % i == 0)
                     {
                         sum = _originalvalue / i;
                         //sum = childsum;
                         dividend = dividend + "," + i+","+sum;
                         if (sum == i)
                         {
                             count++;
                         }
                         else
                         {
                             count = count + 2;
                         }
                     }
                 }
                 return count;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多