【问题标题】:Print all unique combination of factors of a given number打印给定数字的所有唯一因子组合
【发布时间】:2013-02-13 20:53:57
【问题描述】:

打印正整数因子的所有唯一组合的最有效算法是什么。例如,如果给定的数字是 24,那么输出应该是

24*1
12*2
8*3
6*4
6*2*2
4*3*2
3*2*2*2

请注意,当打印 6*4 时,不会打印 4*6。所以基本上这是一个在不考虑顺序的情况下获取唯一子集的问题(一种看待问题的方法)。但目标是拥有一个运行速度最快的函数,因此将因子存储在数据结构中以进行进一步操作可能会消耗更多时间。我已经尝试了我的算法并在下面粘贴了我的代码,但它似乎没有给我想要的结果,我在递归调用中犯了一些错误。你能帮我找出一个有效的方法来做到这一点吗?

public static void printfact(int num){
        int temp=0;
        for(int i=num-1;i>=num/2;i--){
            if(num % i == 0){
                temp = num/i;
                System.out.println(temp + " * " + i);
                if(isprime(i)==false){
                    System.out.print(temp + " * ");
                    printfact(i);
                }
            }
        }
}

【问题讨论】:

  • 感谢 Sanjeev 的问题编辑建议
  • 我认为你需要:en.wikipedia.org/wiki/…找到所有因素后。
  • 6 和 4 都不是 24(或任何其他数字)的质因数,因为它们不是,嗯...质数。
  • @Aubin 这是正确的标题,它不是关于打印主要因素,而是所有因素的组合。看看我给出的例子。

标签: java algorithm recursion time-complexity factors


【解决方案1】:

1) 如果i < numi > num/2,那么num % i == num - i。 (很容易证明。)所以你的for 循环将毫无意义地检查所有大于num/2 的整数,而if 语句只会成功一次,temp == 2。我不认为那是你想要的。

2) 在你解决了这个问题后,递归可能需要产生很多答案。但是您只打印一次temp *。所以输出看起来会有点奇怪。

3) isprime 是不必要的。 num 始终是一个合法因素,无论它是否是质数,只要您遵循以下几点。

4) 最后,您需要弄清楚如何避免多次打印出相同的因式分解。简单的解决方案是仅产生因子单调不增加的分解(如您的示例中所示)。为了做到这一点,递归需要产生具有一些最大因子(这将是先前发现的因子)的因子分解。因此,递归函数应该(至少)有两个参数:因子数和最大允许因子​​。 (您还需要处理我在第 4 点中提到的问题。)

下面的 Python 代码确实(我相信)解决了这个问题,但它仍然做了很多不必要的划分。与 python 风格不同的是,它打印每个分解而不是充当生成器,因为这样更容易翻译成 Java。

# Uses the last value in accum as the maximum factor; a cleaner solution
# would have been to pass max_factor as an argument.
def factors(number, accum=[]):
  if number == 1:
    print '*'.join(map(str, accum))
  else:
    if accum:
      max_factor = min(accum[-1], number)
    else:
      max_factor = number
    for trial_factor in range(max_factor, 1, -1):
      remnant = number / trial_factor
      if remnant * trial_factor == number:
        factors(remnant, accum + [trial_factor,])

可以优化for 语句。例如,一旦计算了remnant,您就知道下一个remnant 必须至少大一个,因此当remnant 较小时,您可以跳过一堆trial_factor 值。

【讨论】:

  • 确切地说,我不确定我是否可以在函数中传递一个带有辅助字符串参数的函数,该参数在递归减少较大元素之前传递要打印的先前因子(例如,如果数字是 40然后是 10*4,然后将 10 和 4 都减少为 5*2*2*2 等等。)。但是我们将如何第一次调用该函数。不过,我无法将其可视化。
  • 不,您不需要同时减少 10 和 4。第一个因素 5 应该稍后出现。所以你应该产生10*410*2*2,然后是8*5,然后是5*4*25*2*2*2。 (当然,这是在其他分解之后,4020*2。)所以问题实际上是如何有效地确定哪些是递归中可能的第一个因素,而不进行大量不必要的模运算。
  • 是的,你是对的,但是如何递归地设备它。能否提供示例代码?
  • @crazyim5:添加了一些python代码,可以考虑伪代码。
  • 谢谢Rici,我修改了一些,自己回答了。让我知道你的想法。
【解决方案2】:

此代码查找数字的所有因数,对它们进行排序(本地和全局):

public class PrimeFactors {

   final SortedSet< List< Integer >> _solutions = new TreeSet<>(
      new Comparator<List<Integer>>(){
         @Override
         public int compare( List<Integer> left, List<Integer> right ) {
            int count = Math.min( left.size(), right.size());
            for( int i = 0; i < count; ++i ) {
               if( left.get(i) < right.get(i)) {
                  return -1;
               }
               if( left.get(i) > right.get(i)) {
                  return +1;
               }
             }
            return left.size() - right.size();
         }});

   public SortedSet< List< Integer >> getPrimes( int num ) {
      _solutions.clear();
      getPrimes( num, new LinkedList< Integer >());
      return _solutions;
   }

   private void getPrimes( int num, List< Integer > solution ) {
      for( int i = num - 1; i > 1; --i ) {
         if(( num % i ) == 0 ) {
            int temp = num / i;
            List< Integer > s = new LinkedList<>();
            s.addAll( solution );
            s.add( temp );
            s.add( i );
            Collections.sort( s );
            if( _solutions.add( s )) { // if not already found
               s = new LinkedList<>();
               s.addAll( solution );
               s.add( temp );
               getPrimes( i, s );
             }
         }
      }
   }
   public static void main( String[] args ) {
      SortedSet< List< Integer >> solutions =
         new PrimeFactors().getPrimes( 24 );
      System.out.println( "Primes of 24 are:" );
      for( List< Integer > l : solutions ) {
         System.out.println( l );
      }
   }
}

输出:

Primes of 24 are:
[2, 2, 2, 3]
[2, 2, 6]
[2, 3, 4]
[2, 12]
[3, 8]
[4, 6]

【讨论】:

  • 感谢您的解决方案。是的,这和我第一次做的差不多,但我们需要消除重复的组合。
  • 不生成重复比事后消除它们要高效得多。正如我所说,关键是始终生成单调不递增的序列;这并不难。
  • 我改进了算法,在遍历分解时消除了已经走过的路径。
【解决方案3】:

尝试这种递归方法,该方法还接受另外 2 个输入,即一个字符串,用于在 for 循环中继承 i 的当前值以执行后续缩减,以及一个临时 int,用于知道何时不打印重复的反转,即 8*3和 3*8。

public static void printFactors(int number, String parentFactors, int parentVal) {
    int newVal = parentVal;
    for (int i = number - 1; i >= 2; i--) {

        if (number % i == 0) {
            if (newVal > i) {
                newVal = i;
            }
            if (number / i <= parentVal && i <= parentVal
                    && number / i <= i) {
                System.out.println(parentFactors + i + "*" + number / i);
                newVal = number / i;
            }

            if (i <= parentVal) {
                printFactors(number / i, parentFactors + i + "*", newVal);
            }
        }

    }

}

并使用 printFactors(12,'',12)
调用此方法 如果您发现这种方法存在缺陷,请告诉我。谢谢!

【讨论】:

  • 这个答案会忽略 1 * n OP 所追求的答案。除此之外,我认为它确实可以捕获所有内容。
【解决方案4】:
vector<unsigned int> GetAllFactors(unsigned int number)
{
    vector<unsigned int> factors;

    for (int i = 2; i <= number; i++)
    {
        if (number % i == 0)
        {
            factors.push_back(i);
        }
    }

    return factors;
}

void DoCombinationWithRepetitionFactors(vector<unsigned int> allFactors, unsigned currentProduct, unsigned targetProduct, vector<unsigned int> currentFactorSet, unsigned currentFactorIndex)
{
    if (currentProduct == targetProduct)
    {
        for (auto a : currentFactorSet)
        {
            cout << a << " , ";
        }

        cout << endl;

        return;
    }


    for (int i = currentFactorIndex; i < allFactors.size(); i++)
    {
        if (currentProduct * allFactors[i] <= targetProduct)
        {
            currentFactorSet.push_back(allFactors[i]);
            DoCombinationWithRepetitionFactors(allFactors, currentProduct * allFactors[i], targetProduct, currentFactorSet, i);
            currentFactorSet.pop_back();
        }
    }
}

【讨论】:

    【解决方案5】:
    bool isprime(int n){
    for(int i=2; i<=sqrt(n); i++)
        if(n%i==0)
            return false;
    return true;
    }
    
    void printprime(int n){
    
    int i,j,y=n;
    
    while(y%2==0){
        cout<<"2 * ";
        y/=2;
    }
    
    for(i=3; i<=sqrt(y); i+=2){
        while(y%i==0){
            cout<<i<<" * ";
            y/=i;
        }
    }
    
    if(y>2)
        cout<<y;
    }
    
    void allfacs(int n){
    
    int i;
    unordered_set<int> done;
    
    for(i=2; i<sqrt(n); i++){
        if(n%i==0){
            cout<<i<<" * "<<n/i<<endl;
    
            if(!isprime(i) && done.find(i) == done.end()){
                done.insert(i);
                printprime(i);
                cout<<n/i<<endl;
            }
            if(!isprime(n/i) && done.find(n/i) == done.end()){
                done.insert(n/i);
                cout<<i<< " * ";
                printprime(n/i);
                cout<<endl;
            }
        }
    }
    }
    

    【讨论】:

      【解决方案6】:

      这是我基于@rici 想法的解决方案。

      def factors(number, max_factor=sys.maxint):
          result = []
      
          factor = min(number / 2, max_factor)
          while factor >= 2:
              if number % factor == 0:
                  divisor = number / factor
      
                  if divisor <= factor and divisor <= max_factor:
                      result.append([factor, divisor])
      
                  result.extend([factor] + item for item in factors(divisor, factor))
      
              factor -= 1
      
          return result
      
      print factors(12) # -> [[6, 2], [4, 3], [3, 2, 2]]
      print factors(24) # -> [[12, 2], [8, 3], [6, 4], [6, 2, 2], [4, 3, 2], [3, 2, 2, 2]]
      print factors(157) # -> []
      

      【讨论】:

        【解决方案7】:

        我有一个解决方案,在 C/C++ 中没有递归、排序或堆栈。

        #include <vector>
        #include <iostream>
        
        // For each n, for each i = n-1 to 2, try prod = prod*i, if prod < N.
        
        int
        g(int N, int n, int k)
        {
                int i = k;
                int prod = n;
                std::vector<int> myvec;
        
                myvec.push_back(n);
                while (i > 1) {
                        if (prod * i == N) {
                                prod = prod*i;
                                myvec.push_back(i);
                                for (auto it = myvec.begin();
                                        it != myvec.end(); it++) {
                                        std::cout << *it << ",";
                                }
                                std::cout << std::endl;
                                return i;
                        } else if (prod * i < N) {
                                prod = prod*i;
                                myvec.push_back(i);
                        } else { i--;}
                }
        
                return k;
        }
        
        void
        f(int N)
        {
                for (int i = 2; i <= N/2; i++) {
                        int x = g(N, i, i-1);
                        // Extract all possible factors from this point
                        while (x > 0) {
                                x = g(N, i, x-1);
                        }
                }
        }
        
        int
        main()
        {
                f(24);
        
                return 0;
        }
        

        输出是这样的:

        $ ./a.out
            3,2,2,2,
            4,3,2,
            6,4,
            6,2,2,
            8,3,
            12,2,
        

        【讨论】:

          【解决方案8】:

          我想出了这个,看起来很容易阅读和理解。希望对您有所帮助!

          def getFactors(num):
          
              results = []
          
              if num == 1 or 0:
                  return [num]
          
              for i in range(num/2, 1, -1):
          
                  if (num % i == 0):
                      divisor = num / i
          
                      if(divisor <= i):
                          results.append([i, divisor])
          
                      innerResults = getFactors(divisor)
          
                      for innerResult in innerResults:
                          if innerResult[0] <= i:
                              results.append([i] + innerResult)
          
              return results
          

          【讨论】:

            【解决方案9】:
            #include<bits/stdc++.h>
            using namespace std;
            int n;
            // prod = current product of factors in combination vector
            // curr = current factor
            void fun(int curr, int prod, vector<int> combination )
            {
                if(prod==n)
                {
                    for(int j=0; j<combination.size(); j++)
                    {
                        cout<<combination[j]<<" ";
                    }
                    cout<<endl; 
                    return;
                }
            
                for(int i=curr; i<n; i++)
                {
                    if(i*prod>n) break;
                    if(n%i==0)
                    {
                        combination.push_back(i);
                        fun(i, i*prod, combination);
                        combination.resize(combination.size()-1);
                    }
                }
            }
            
            int main()
            {
                cin>>n;
                vector<int> combination;
                fun(2, 1, combination);
                cout<<1<<" "<<n;
                return 0;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-11-12
              • 1970-01-01
              • 1970-01-01
              • 2020-08-03
              • 2014-10-07
              • 2021-11-29
              • 1970-01-01
              相关资源
              最近更新 更多