【问题标题】:For a given number N, how do I find x, S.T product of (x and no. of factors to x) = N?对于给定的数字 N,我如何找到 (x 和 x 的因子数) = N 的 x,S.T 乘积?
【发布时间】:2017-04-13 01:22:58
【问题描述】:

要找到数的因数,我使用函数void primeFactors(int n)

# include <stdio.h>
# include <math.h>
# include <iostream>
# include <map>

using namespace std; 
// A function to print all prime factors of a given number n
map<int,int> m;
void primeFactors(int n)
{
    // Print the number of 2s that divide n
    while (n%2 == 0)
    {
        printf("%d ", 2);
        m[2] += 1;
        n = n/2;
    }

    // n must be odd at this point.  So we can skip one element (Note i = i +2)
    for (int i = 3; i <= sqrt(n); i = i+2)
    {
        // While i divides n, print i and divide n
        while (n%i == 0)
        {
            int k = i;
            printf("%d ", i);
            m[k] += 1; 
            n = n/i;
        }
    }

    // This condition is to handle the case whien n is a prime number
    // greater than 2
    if (n > 2)
        m[n] += 1; 
        printf ("%d ", n);


   cout << endl;
}

/* Driver program to test above function */
int main()
{
    int n = 72;
    primeFactors(n);
    map<int,int>::iterator it;
    int to = 1;
    for(it = m.begin(); it != m.end(); ++it){
        cout << it->first << " appeared " << it->second << " times "<< endl;
        to *= (it->second+1);
    }
    cout << to << " total facts" << endl;
    return 0;
}

你可以在这里查看。 Test case n = 72http://ideone.com/kaabO0

如何使用上述算法解决上述问题。 (可以进一步优化吗?)。我也必须考虑大数字。

我想做什么.. 以 N = 864 为例,我们发现 X = 72 为 (72 * 12 (因子数)) = 864)

【问题讨论】:

标签: c++ algorithm optimization factors


【解决方案1】:

有一个大数的素数分解算法,但实际上它并不经常用于编程比赛。
我解释了 3 种方法,你可以使用这个算法来实现。
如果你实现了,我建议解决this problem
注意:在这个答案中,我使用整数 Q 作为查询数。

每个查询的 O(Q * sqrt(N)) 解决方案
你的算法的时间复杂度是O(n^0.5)
但是您正在使用 int(32 位)实现,因此您可以使用 long long 整数。
这是我的实现:http://ideone.com/gkGkkP

O(sqrt(maxn) * log(log(maxn)) + Q * sqrt(maxn) / log(maxn)) 算法
您可以减少循环次数,因为整数 i 不需要合数。
因此,您只能在循环中使用素数。

算法:

  1. 用 Eratosthenes 的筛子计算所有素数
  2. 在查询中,循环查找 i(i

更高效的算法
世界上有更高效的算法,但在编程比赛中并不经常使用。
如果您在互联网或维基百科上查看“整数分解算法”,您可以找到 Pollard's-rho 或通用数字字段筛等算法。

【讨论】:

    【解决方案2】:

    好吧,我给你看代码。

    # include <stdio.h>
    # include <iostream>
    # include <map>
    using namespace std;
    const long MAX_NUM = 2000000;
    long prime[MAX_NUM] = {0}, primeCount = 0;
    bool isNotPrime[MAX_NUM] = {1, 1}; // yes. can be improve, but it is useless when sieveOfEratosthenes is end
    void sieveOfEratosthenes() {
        //@see https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
        for (long i = 2; i < MAX_NUM; i++) { // it must be i++
            if (!isNotPrime[i]) //if it is prime,put it into prime[]
                prime[primeCount++] = i;
            for (long j = 0; j < primeCount && i * prime[j] < MAX_NUM; j++) { /*foreach prime[]*/
    //            if(i * prime[j] >= MAX_NUM){ // if large than MAX_NUM break
    //                break;
    //            }
                isNotPrime[i * prime[j]] = 1;  // set i * prime[j] not a prime.as you see, i * prime[j]
                if (!(i % prime[j])) //if this prime the min factor of i,than break.
                                     // and it is the answer why not i+=( (i & 1) ? 2 : 1).
                                     // hint : when we judge 2,prime[]={2},we set 2*2=4 not prime
                                     //        when we judge 3,prime[]={2,3},we set 3*2=6 3*3=9 not prime
                                     //        when we judge 4,prime[]={2,3},we set 4*2=8 not prime (why not set 4*3=12?)
                                     //        when we judge 5,prime[]={2,3,5},we set 5*2=10 5*3=15 5*5=25 not prime
                                     //        when we judge 6,prime[]={2,3,5},we set 6*2=12 not prime,than we can stop
                                     // why not put 6*3=18 6*5=30 not prime? 18=9*2 30=15*2.
                                     // this code can make each num be set only once,I hope it can help you to understand
                                     // this is difficult to understand but very useful.
                    break;
            }
        }
    }
    void primeFactors(long n)
    {
        map<int,int> m;
        map<int,int>::iterator it;
        for (int i = 0; prime[i] <= n; i++) // we test all prime small than n , like 2 3 5 7... it musut be i++
        {
            while (n%prime[i] == 0)
            {
                cout<<prime[i]<<" ";
                m[prime[i]] += 1;
                n = n/prime[i];
            }
        }
        cout<<endl;
        int to = 1;
        for(it = m.begin(); it != m.end(); ++it){
            cout << it->first << " appeared " << it->second << " times "<< endl;
            to *= (it->second+1);
        }
        cout << to << " total facts" << endl;
    
    }
    int main()
    {
        //first init for calculate all prime numbers,for example we define MAX_NUM = 2000000
        // the result of prime[] should be stored, you primeFactors will use it
        sieveOfEratosthenes();
        //second loop for i (i*i <= n and i is a prime number). n<=MAX_NUM
        int n = 72;
        primeFactors(n);
        n = 864;
        primeFactors(n);
        return 0;
    }
    

    【讨论】:

    • "bool isNotPrime[MAX_NUM]" - (groan) - 怜悯...并为此使用std::vector&lt;bool&gt;,空间要求比bools 的数组小8倍.由于您可能希望在最大数量上攀升,因此空间复杂度开始变得重要(cpu 缓存局部性,素数随着您的价值攀升而变得稀有的事实等等)。
    • for (long i = 2; i &lt; MAX_NUM; i++) - (双重呻吟)。为什么i++?为什么不i+=( (i &amp; 1) ? 2 : 1)。解释:当i==2(第一次通过循环)时,您将i增加1。下一次通过,您以2步进行。(for (int i = 0; prime[i]*prime[i] &lt;= n; i ++)也是如此)
    • for (long j = 0; j &lt; primeCount &amp;&amp; i * prime[j] &lt; MAX_NUM; j++) ... 在筛子中...哦,来吧,伙计,真的。 for(long j = i*i; j&lt;MAX_NUM; j+=i) { ... 有什么问题(j=i*i 的解释 - 低于 i*i 的任何素性确定已经在 i 的较低值下进行)。
    • @AdrianColomitchi prime[i]*prime[i]
    • @AdrianColomitchi 我已经在代码中添加了一些注释。或者您可以自己编写代码并调试它。我希望它可以回答您的问题:)
    【解决方案3】:

    我在性能方面的最佳表现,而不会过度使用特殊算法。

    Erathostenes' seive - 下面的复杂性是 O(N*log(log(N))) - 因为内部 j 循环从 i*i 而不是 i 开始。

    #include <vector>
    using std::vector;
    
    void erathostenes_sieve(size_t upToN, vector<size_t>& primes) {
      primes.clear();
      vector<bool> bitset(upToN+1, true); // if the bitset[i] is true, the i is prime
      bitset[0]=bitset[1]=0;
    
      // if i is 2, will jump to 3, otherwise will jump on odd numbers only
      for(size_t i=2; i<=upToN; i+=( (i&1) ? 2 : 1)) {
        if(bitset[i]) { // i is prime
          primes.push_back(i);
          // it is enough to start the next cycle from i*i, because all the 
          // other primality tests below it are already performed:
          // e.g:
          // - i*(i-1) was surely marked non-prime when we considered multiples of 2
          // - i*(i-2) was tested at (i-2) if (i-2) was prime or earlier (if non-prime)
          for(size_t j=i*i; j<upToN; j+=i) {
             bitset[j]=false; // all multiples of the prime with value of i
                              // are marked non-prime, using **addition only**
          }
        }
      }
    }
    

    现在基于 primes(设置在 sorted 向量中)进行分解。在此之前,让我们来看看sqrt 很昂贵但大量乘法并不昂贵的神话。

    首先,让我们注意sqrt is not that expensive anymore:在较旧的 CPU-es (x86/32b) 上,它曾经是除法的两倍(而 modulo 操作 除法),在较新的架构上,CPU 成本是相等的。由于因式分解就是一遍又一遍地进行% 操作,因此人们可能仍会不时考虑sqrt(例如,是否以及何时使用它可以节省CPU 时间)。

    例如,假设primes 具有10000 entries,假设N=65537(这是第6553 个素数)的以下代码

    size_t limit=std::sqrt(N);
    size_t largestPrimeGoodForN=std::distance(
      primes.begin(), 
      std::upper_limit(primes.begin(), primes.end(), limit) // binary search
    );
    // go descendingly from limit!!!
    for(int i=largestPrimeGoodForN; i>=0; i--) { 
       // factorisation loop
    }
    

    我们有:

    • 1 sqrt(等于 1 modulo),
    • 1 在10000 条目中搜索 - 最多 14 步,每个步骤涉及 1 次比较、1 次右移除以 2 和 1 次递增/递减 - 所以假设成本等于 14-20 次乘法(如果有的话)
    • 因为std::distance而产生了1个差异。

    那么,最大成本 - 1 div 和 20 muls?我很慷慨。

    另一边:

    for(int i=0; primes[i]*primes[i]<N; i++) {
      // factorisation code
    }
    

    看起来更简单,但由于N=65537 是素数,我们将遍历所有循环直到i=64(我们将找到导致循环中断的第一个素数) - 总共 65 次乘法.
    尝试使用更高的素数,我向您保证 1 sqrt + 1 二进制搜索的成本比所有以更简单的循环形式进行的乘法运算更好地利用 CPU 周期被吹捧为更好的性能解决方案


    所以,回到分解代码:

    #include <algorithm>
    #include <math>
    #include <unordered_map>
    void factor(size_t N, std::unordered_map<size_t, size_t>& factorsWithMultiplicity) {
          factorsWithMultiplicity.clear();
    
       while( !(N & 1) ) { // while N is even, cheaper test than a '% 2'
         factorsWithMultiplicity[2]++;
         N = N >> 1; // div by 2 of an unsigned number, cheaper than the actual /2
       }
       // now that we know N is even, we start using the primes from the sieve
       size_t limit=std::sqrt(N); // sqrt is no longer *that* expensive,
    
       vector<size_t> primes;
       // fill the primes up to the limit. Let's be generous, add 1 to it
       erathostenes_sieve(limit+1, primes);
       // we know that the largest prime worth checking is
       // the last element of the primes.
       for(
         size_t largestPrimeIndexGoodForN=primes.size()-1;
         largestPrimeIndexGoodForN<primes.size(); // size_t is unsigned, so after zero will underflow
         // we'll handle the cycle index inside
       ) {
         bool wasFactor=false;
         size_t factorToTest=primes[largestPrimeIndexGoodForN];
         while( !( N % factorToTest) ) {
           wasFactor=true;// found one
           factorsWithMultiplicity[factorToTest]++;
           N /= factorToTest;
         }
         if(1==N) { // done
            break;
         }
         if(wasFactor) { // time to resynchronize the index
           limit=std::sqrt(N);
           largestPrimeIndexGoodForN=std::distance(
              primes.begin(),
              std::upper_bound(primes.begin(), primes.end(), limit)
           );
         }
         else { // no luck this time
           largestPrimeIndexGoodForN--;
         }
       } // done the factoring cycle
       if(N>1) { // N was prime to begin with
         factorsWithMultiplicity[N]++;
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-07
      • 1970-01-01
      • 2016-05-21
      • 2023-02-10
      • 2019-09-17
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      相关资源
      最近更新 更多