一种查找给定数量的种子根的算法

时间:2013-10-17 18:32:14

标签: algorithm

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

  

考虑数字123,数字及其数字(123 * 1 * 2 * 3 = 738)的乘积是738.因此,123是738的种子根。写一个程序接受一个数字并找到所有可能的种子根。例如,如果用户输入4977,则答案应为79和711。

我想到了一种方式:

  1. 找到2到9之间的所有数字,除以数字。

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

  3. 但是,这假定数字不会重复,其次它不会打印所有数字,例如4977,它可以找到79但不会找到711。

    有更好的方法吗?

6 个答案:

答案 0 :(得分:4)

我的方法是这样的。它是一个递归算法,它使用一个集合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;
        }
    }
}

答案 1 :(得分:4)

另一个解决方案是检查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;
    }
  }
}

答案 2 :(得分:0)

首先,找出影响数字的所有方法:

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

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

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

运行所有这些因素,看看是否有正确的形式。

“正确形式”是一个因子分解,其中一个因子具有与因子数相同的位数(少于一个),而其他因子等于数字

这提示了一种在找到它们时过滤分解因素的方法,因为一旦找到

  • 两个两位数或更高的因子(因为一个因子的解决方案可能有一个以上的数字加上所有其他因素只有一个数字)-OR -
  • 比原始数字中的位数更多的单位数因子(因为一个因子的数字永远不会超过原始数字)
  • 可能还有更多

分解不起作用。

以下是一些对数字进行分解的方法的链接: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;
    }
}

答案 3 :(得分:0)

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

更新:找到376,352,349 - &gt;需要27秒153,642,082,955,760。你呢?我不知道这是否好。

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;
}

答案 4 :(得分:0)

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;
    }
    }

*

答案 5 :(得分:0)

<块引用>

实现一个程序来找出一个数字是否是另一个数字的种子。 如果 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 
    }
}