【问题标题】:An algorithm to find the seed root of a given number找到给定数字的种子根的算法
【发布时间】:2013-10-17 18:32:14
【问题描述】:

我在Glassdoor遇到了这个问题,并尝试实现。问题如下-

考虑一个数字 123,该数字与它的数字的乘积 (123*1*2*3 = 738) 是 738。因此,123 是 738 的种子根。编写程序接受一个数字并找出所有可能的种子根。例如,如果用户输入 4977,那么答案应该是 79 和 711。

我想到了一个办法:

  1. 找出从 2 到 9 中除数的所有数字。

  2. 然后从最大的数字开始(在步骤 1 中找到的数字中),找到组成数字的数字,然后打印这些数字的所有排列。

但是,这假设数字不重复,其次它不会像 4977 那样打印所有数字,它可以找到 79 但找不到 711。

有更好的方法吗?

【问题讨论】:

    标签: algorithm


    【解决方案1】:

    我的方法是这样的。这是一种递归算法,它使用集合 S,它是一个包含从 2 到 9 的数字的多重集合,可能是多次。

    try (N, S, digit) {
        for d = digit, digit-1, ..., 2 {
            if N is divisible by d then {
                S' = S + {d};
                if N/d is composed of all the digits in S', perhaps with
                   some 1's thrown in, then N/d is an answer;
                try (N/d, S', d);
            }
        }
    }
    

    那么为原来的数字

    try (originalN, empty-set, 9);
    also check originalN to see if it has only 1 digits (11, 11111, etc.); if so, then it's also an answer
    

    我认为这可行,但我可能错过了一些边界情况。

    对于 4977,try(4977, empty, 9) 会发现 4977 可以被 9 整除,因此它调用 try(553, {9}, 9)。内部try发现553能被7整除,553/7 = 79;此时 S' = {7, 9},算法检查 79 是否由数字 {7, 9} 组成,结果成功。不过,该算法仍在继续。最终我们将返回到外部try,它会在某个时候尝试d = 7,并且4977/7 = 711,当我们进行检查时,S' = {7} 和711 由7 和一些组成1,所以这也是一个答案。

    编辑:我已经包含了一个完整的 C++ 函数:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    struct digitMultiset {
        int counts[10];  // note: the [0] and [1] elements are not used
    };
    
    static const digitMultiset empty = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
    
    bool hasDigits (const int n, digitMultiset& s) {
        digitMultiset s2 = empty;
        int temp = n;
        int digit;
        while (temp > 0) {
            digit = temp % 10;
            if (digit == 0) return false;
            if (digit != 1) s2.counts[digit]++;
            temp /= 10;
        }
        for (int d = 2; d < 10; d++)
            if (s2.counts[d] != s.counts[d]) return false;
        return true;
    }
    
    void tryIt (const int currval, const digitMultiset& s,
                const int digit, vector<int>& answers) {
        digitMultiset newS;
        for (int d = digit; d >= 2; d--) {
            if (currval % d == 0) {
                int quotient = currval / d;
                newS = s;
                newS.counts[d]++;
                if (hasDigits (quotient, newS))
                    answers.push_back (quotient);
                tryIt (quotient, newS, d, answers);
            }
        }
    }
    
    void seedProduct (const int val) {
        vector<int> answers;
        tryIt (val, empty, 9, answers);
        int temp = val;
        bool allOnes = true;
        while (temp > 0)  {
            if (temp % 10 != 1) {
                allOnes = false;
                break;
            }
            temp /= 10;
        }
        if (allOnes)
            answers.push_back(val);
    
        int count = answers.size();
        if (count > 0)  {
            if (count == 1)
                cout << val << " has seed product " << answers[0] << endl;
            else  {
                cout << val << " has " << count << " seed products: ";
                for (int& ans : answers)
                    cout << ans << " ";
                cout << endl;
            }
        }
    }
    

    【讨论】:

    • 我不知道你是怎么想的,但它似乎有效。让其他人评论一下
    • 请注意,如果您找到答案,您不会退出递归。只有当你发现 N 不能被除 1 以外的任何数字整除时,你才会退出递归。
    • 对,它可能会返回多个答案,这是要求的一部分。
    【解决方案2】:

    另一种解决方案是检查 n 的每个除数,看看这是否可能是种子。要检查所有除数,您只需检查 n 的平方根。所以这个算法在 O(sqrt(n)) 中运行。能不能快点?

    这里有一个简单的 C++ 程序来演示这个想法。

    #include<iostream>
    
    using namespace std;
    
    int prod(int n){
      int res=n;
      while(n!=0){
        res*=n%10;
        n/=10;
      }
      return res;
    }
    
    int main(){
      int n;
      cin >> n;
    
      for(int i=1;i*i<=n;++i){
        if(n%i==0){
          if(prod(i)==n)
            cout << i << endl;
          if(n/i!=i && prod(n/i)==n)
            cout << n/i << endl;
        }
      }
    }
    

    【讨论】:

    • 我看到的结果是,当没有答案时,如果数字大于 25000 左右,我的算法会快很多。 6 位数字大约快 2 倍,7 位数字快 4 倍。当至少有一个答案时,您的算法对于七位数字的速度大约快 2 倍,而对于较小的数字,该比率更大。我发现大约 99% 或更多的值没有解决方案;所以对于一个循环遍历某个范围内所有可能的输入值的程序,如果它涉及 5 位或更大的数字,我的应该会快很多。
    • 好吧,如果想要遍历某个(1 到 n)范围内的所有可能值,像 for(n in range) resultat[prod(n)].push_back(n) 这样的东西可能会快得多。
    • +1 确实是最容易理解的解决方案。谢谢! @Blabla404
    【解决方案3】:

    首先,找出所有因数分解的方法:

    100 - 2 * 50 - 4 * 25 - 2 * 2 * 25 - ...等等... - 2 * 2 * 5 * 5

    如果数字中有任何 1 位,请使用这些添加一些因素:

    • 1 * 2 * 50
    • 1 * 4 * 25
    • ...等等

    遍历所有这些因式分解,看看其中是否有正确的形式。

    “正确的形式”是一种因式分解,其中一个因子的位数与因子数相同(减去一个),而其他因子的位数相等

    这建议了一种在找到分解时过滤分解的方法,因为一旦找到

    • 两个两位数或更高的因子(因为一个因子的解 consts 可能有不止一个数字加上所有其他因子正好有一个数字)-或-
    • 个位数因子多于原始数字的位数(因为一个因子的位数永远不能多于原始数字)
    • 可能还有更多

    因式分解不起作用。

    这里是一些分解数字的方法的链接:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.1230&rep=rep1&type=pdf

    这里有一些代码可以做到这一点。没有关于在所有情况下都正确的承诺。当它无法解决时,使用蛮力分解和一些过滤器来短路过程。

    package com.x.factors;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Factors {
    
        private List<Long> solutions;
        private final long original;
        private final int originalDigitCount;
        private List<Long> primes = new ArrayList<Long>();
    
        public Factors(long original) {
            this.original = original;
            this.originalDigitCount = ("" + original).length();
        }
    
        public List<Long> findSeeds() {
            // Consider a number 123, the product of the number with its digits (123*1*2*3 = 738) is 738. Therefore, 123 is
            // the seed product of 738. Write a program to accept a number and find all possible seed products. For example,
            // If the user entered 4977 then the answer should be 79 and 711.
    
            solutions = new ArrayList<Long>();
    
            // filter out numbers that can't have seeds
    
            // Number must be positive (and not zero)
            if (original <= 0) {
                return solutions;
            }
    
            // Find a number with a 0 digit in it
            long temp = original;
            while (temp > 0) {
                if (temp % 10 == 0) {
                    return solutions;
                }
                temp = temp / 10;
            }
    
            collectFactors(original, new ArrayList<Long>(), 0, 0);
    
            return solutions;
        }
    
        private void collectFactors(long num, List<Long> factors, int factorCount, int doubleDigitFactorCount) {
            if (primes.contains(num)) {
                return;
            }
    
            // The seed can't have more digits than the original number. Thus if we find more factors excluding
            // the seed than that, this can't be a solution.
            if (factorCount > originalDigitCount) {
                return;
            }
    
            boolean isPrime = true; // check whether num is prime
            int newDoubleDigitFactorCount = 0;
            for (long i = num / 2; i > 1; --i) {
    
                // Once we have one factor 2 digits or over, it has to be the seed, so there is no use trying
                // any more double digit numbers as only single digits are needed.
                if (i > 9) {
                    if (doubleDigitFactorCount > 0) {
                        return; // short circuit because of two many non-one-digit factors
                    }
                    newDoubleDigitFactorCount = 1;
                } else {
                    newDoubleDigitFactorCount = 0;
                }
    
                long remdr = num / i;
                if (remdr * i == num) { // is it a factor?
                    isPrime = false; // it has a factor, its not prime
    
                    // add this new factor into the list
                    if (factors.size() <= factorCount) {
                        factors.add(i);
                    } else {
                        factors.set(factorCount, i);
                    }
    
                    // found a list of factors ... add in the remainder and evaluate
                    if (factors.size() <= factorCount + 1) {
                        factors.add(remdr);
                    } else {
                        factors.set(factorCount + 1, remdr);
                    }
                    long seed = evaluate(factors, factorCount + 2);
                    if (seed > 0) {
                        if (solutions.contains(seed)) {
                            continue;
                        }
                        solutions.add(seed);
                    }
    
                    collectFactors(remdr, factors, factorCount + 1, doubleDigitFactorCount + newDoubleDigitFactorCount);
                }
            }
            if (isPrime) { // if its prime, save it
                primes.add(num);
            }
            return;
        }
    
        /* package */long evaluate(List<Long> factors, int factorCount) {
            // Find seed, the largest factor (or one of them if several are the same)
            long seed = 0; // Note seed will be larger than 0
            int seedIndex = 0;
            for (int i = 0; i < factorCount; ++i) {
                if (factors.get(i) > seed) {
                    seed = factors.get(i);
                    seedIndex = i;
                }
            }
    
            // Count the digits in the seed, see if there are the right number of factors. Ignore 1's
            boolean[] factorUsed = new boolean[factorCount]; // start off as all false
            int seedDigitCount = 0;
            long temp = seed;
            while (temp > 0) {
                if (temp % 10 != 1) {
                    ++seedDigitCount;
                }
                temp = temp / 10;
            }
            if (seedDigitCount != factorCount - 1) {
                return 0; // fail - seed digit count doesn't equal number of single digit factors
            }
    
            // See if all the seed's digits are present
            temp = seed;
            factorUsed[seedIndex] = true;
            while (temp > 0) {
                int digit = (int) (temp % 10);
                if (digit != 1) { // 1's are never in the factor array, they are just freely ok
                    boolean digitFound = false;
                    for (int digitIndex = 0; digitIndex < factorCount; ++digitIndex) {
                        if (digit == factors.get(digitIndex) && !factorUsed[digitIndex]) {
                            factorUsed[digitIndex] = true;
                            digitFound = true;
                            break;
                        }
                    }
                    if (!digitFound) {
                        return 0; // fail, a digit in the seed isn't in the other factors
                    }
                }
                temp = temp / 10;
            }
    
            // At this point we know there are the right number of digits in the seed AND we have
            // found all the seed digits in the list of factors
            return seed;
        }
    }
    

    【讨论】:

    • “遍历所有这些因式分解”可能需要一段时间,除非您有改进的想法...
    • @Lee Meador:Dukeling 是对的。另外,如果你有一个数字说81,那么你可以写成9*9,3*3*9,3*3*3*3。它们都是1 位数字.我们将如何处理此类案件?
    • 除了其中一个之外的所有内容都不是“正确的形式”。您必须编写代码来检测一个因式分解,其中一个因式的位数与因式数(少一个)相同,而其他因式等于位数。 9 * 9 符合该标准。 9 是数字,数字列表是 [9] 长度为 1 的列表。
    • 请注意,找到所有分解是一个任意难题。例如,简单的方法是遍历所有整数,直到数字的 1/2,检查该整数是否是一个因数,如果是,则递归查找原始数除以该因数的因数。对于 100 位数字,这将需要很长时间。
    • 一直在开玩笑。尝试 497745674,它没有种子,但我需要大约 10 秒才能找到它。或者试试 9333333,它有一个种子,里面有很多 1。 (@ajb 可能有更快的算法)
    【解决方案4】:

    这是一个简单的程序。在 1 秒内获得176852740608 的种子根。 Live demo.

    更新:找到 376,352,349 -> 153,642,082,955,760 需要 27 秒。你们呢?我不知道这样好不好。

    Update2:@ajb 有一个更快的答案,至少在我做的实验中是这样。但是这个答案的优点是更简单!

    #include<iostream>
    using namespace std;
    
    typedef long long int Big_Int; // To get a 64-bit int
    
    void root_stem(const Big_Int r, const Big_Int product_of_my_digits, const Big_Int target) {
    
            // There are two rules we can use to prune:
            //
            // First: The product_of_my_digits must divide into the target.
            //        If not, return
            // Second: The products produced lower in the search true will always be higher
            //         than those above. Therefore, we should return early if
            //         my_seed_product is larger than the target
    
            if (target % product_of_my_digits != 0)
                    return;
    
            Big_Int my_seed_product = r * product_of_my_digits;
    
            if(my_seed_product >= target) {
                    if (my_seed_product == target) {
                            cout << r << "\t->\t" << my_seed_product << endl;
                    }
                    return;
            }
            // print all roots, with their products, between 10*r and 10*r + 9
            for(Big_Int digit_to_append = 1; digit_to_append<=9; ++digit_to_append) {
                    root_stem(r*10 + digit_to_append, product_of_my_digits*digit_to_append, target);
            }
    }
    
    int main() {
            root_stem(0,1, 4977);
            root_stem(0,1, 24562368);
            root_stem(0,1, 176852740608);
            return 0;
    }
    

    【讨论】:

      【解决方案5】:

      176852740608 不到 1 秒。找出所有因素,然后检查特定因素是否为种子根。

      import java.util.ArrayList;
      
      public class Number {
          long value;
          public ArrayList<Long> factors= new ArrayList<Long>();
      
          public Number(long a){
              value = a;
              for(long i=1;i<= Math.sqrt(a);i++){
                  if(a%i==0)
                      {
                      factors.add(i);
                      if((a/i)!=i)
                          factors.add(a/i);
                      }
              }
              System.out.println(factors);
          }
      
      
      public static void main(String args[]){
          long x = 24562368L;
          Number N = new Number(x);
          for(long i : N.factors){
              long ProductOfDigits = 1;
              long k = i;
              while(k!=0){
                  ProductOfDigits = ProductOfDigits*(k%10);
                  k = k/10;
              }
              if(i*ProductOfDigits == N.value)
                  System.out.println(i);
          }
      }
      }
      

      【讨论】:

        【解决方案6】:
        public class Seeds
            {
                public static void main(String[] args)
                {
                     int num=4977;
                     if(!seed(num).isEmpty())
                         System.out.println(seed(num));
                     else
                         System.out.println("no seed number");
                }
                public static List<Integer> seed(int num)
                {  List<Integer> list=new ArrayList<Integer>();
                    for(int i=1; i<=num; i++)
                        {
                        if(num%i==0)
                        {
                            int factor_digit=1;
                            int factor = i;
                            factor_digit=factor_digit*factor;
        
                    // when i is the factor, find factors of i and multiply them together to form number        
                    while(factor>=1)
                    {
                        factor_digit = factor_digit * (factor%10); 
                        factor = factor/10;     
                    }
                    if(factor_digit== num) 
                        list.add(i);
                }
        
            }
                    return list;
            }
            }
        
        *
        

        【讨论】:

          【解决方案7】:

          实现一个程序来确定一个数字是否是另一个数字的种子。 如果将 X 乘以其每个数字等于 Y,则数字 X 被称为数字 Y 的种子。 例如:123 是 738 的种子,因为 12312*3 = 738 */

          class SeedNumber 
          {
              public static void main(String[] args) 
              {
                  int seed = 45;
                  int num = 900;
                  int seedNum = seed;
                  int check=1;
                  for(check=check*seed;seed>0;seed /=10)
                  {
                      int rem = seed % 10;
                      check = check * rem;              
                  }
                  System.out.println(check);
                  if(check == num)
                      System.out.println(seedNum+" is a seed of "+num);
                  else
                      System.out.println(seedNum+" is not a seed of "+num);
                  // Implement your code here 
              }
          }
          

          【讨论】:

            猜你喜欢
            • 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
            相关资源
            最近更新 更多