反向因子

时间:2010-04-16 11:35:03

标签: algorithm math

嗯,我们都知道,如果给出N,则很容易计算N!。但反过来呢?

N!给出了你即将找到N - 这可能吗?我很好奇。

17 个答案:

答案 0 :(得分:17)

  1. 设置X=1
  2. 生成F=X!
  3. F =输入?如果是,则X为N。
  4. 如果没有,请设置X=X+1,然后再次从#2开始。
  5. 您可以使用F的上一个结果进行优化,以计算新Fnew F = new X * old F)。

    它的速度与反方向一样快,如果不是更快,因为除法通常比乘法更长。除了A之外,给定的因子A!保证所有整数都小于A作为因子,因此您花费的时间与计算运行因子的时间一样多。< / p>

答案 1 :(得分:14)

如果你有Q = N!在二进制中,计算尾随零。拨打这个号码J。

如果N是2K或2K + 1,那么J等于2K减去二进制表示为2K的1的数量,所以反复加1,直到你添加的1的数量等于数量为1结果是1。

现在你知道2K,N是2K或2K + 1。要知道它是哪一个,在2K + 1中计算最大素数(或任何素数,真实)的因子,并用它来测试Q =(2K + 1)!。

例如,假设Q(二进制)是

1111001110111010100100110000101011001111100000110110000000000000000000

(对不起,它太小了,但是我没有方便操作更大数字的工具。)

有19个尾随零,这是

10011

现在增加:

1: 10100
2: 10101
3: 10110 bingo!

所以N是22或23.我需要23的素数因子,而且,我必须选择23(碰巧2K + 1是素数,但我没有计划,并且不需要)。所以23 ^ 1应该除23 !,它不分Q,所以

N=22

答案 2 :(得分:12)

int inverse_factorial(int factorial){
    int current = 1;
    while (factorial > current) {
        if (factorial % current) {
            return -1; //not divisible
        }
        factorial /= current;
        ++current;
    }
    if (current == factorial) {
        return current;
    }
    return -1;
}

答案 3 :(得分:9)

是。我们打电话给你的输入x。对于较小的x值,您可以尝试n的所有值,看看是否为n! = x。对于较大的x,您可以对n进行二进制搜索以找到正确的n(如果存在的话)。注意我们有帽子! ≈e^(n ln n - n)(这是Stirling's approximation),所以你大致知道在哪里看。

问题当然是,很少有数字是阶乘;所以你的问题只适用于一小部分输入。如果您的输入很小(例如,适合32位或64位整数),查找表将是最佳解决方案。

(你当然可以考虑反转Gamma function的更普遍的问题。再次,二分搜索可能是最好的方式,而不是分析。我很高兴在这里显示错误。)

编辑:实际上,如果你不确定x是一个阶乘数,你可能无法通过使用斯特林近似的二分搜索获得那么多(或任何东西)或Gamma功能,简单的解决方案。逆因子增长慢于对数(这是因为阶乘是超指数),你必须进行任意精度算术才能找到阶乘并将这些数乘以。

例如,请参阅Draco Ater的答案,即(当扩展到任意精度算术时)将适用于所有x。更简单,甚至可能更快,因为乘法比分裂快,是Dav的答案,这是最自然的算法......这个问题是简单的另一个胜利,它出现了。 : - )

答案 4 :(得分:7)

好吧,如果你知道 M确实是某个整数的阶乘,那么你可以使用

n! = Gamma(n+1) = sqrt(2*PI) * exp(-n) * n^(n+1/2) + O(n^(-1/2))

你可以解决这个问题(或者,真的,解决ln(n!) = ln Gamma(n+1))并找到最接近的整数。 它仍然是非线性的,但您可以通过迭代轻松获得近似解(实际上,我希望n^(n+1/2)因子足够了。)

答案 5 :(得分:6)

多种方式。使用查找表,使用二进制搜索,使用线性搜索...

查找表显而易见:

for (i = 0; i < MAX; ++i)
    Lookup[i!] = i; // you can calculate i! incrementally in O(1)

例如,你可以使用hash tables来实现它,或者如果你使用C ++ / C#/ Java,它们就有自己的类似哈希表的容器。

如果您必须多次执行此操作并且每次必须快速执行此操作,这非常有用,但您可以花一些时间来构建此表。

二进制搜索:假设数字为m = (1 + N!) / 2m!大于N!吗?如果是,请减少1和m!之间的搜索,否则在m! + 1N!之间减少搜索。递归地应用这个逻辑。

当然,这些数字可能非常大,您最终可能会做很多不必要的操作。更好的想法是使用二分搜索在1和sqrt(N!)之间进行搜索,或尝试找到更好的近似值,尽管这可能并不容易。考虑研究gamma function

线性搜索:在这种情况下可能是最好的。计算1*2*3*...*k,直到产品等于N!并输出k

答案 6 :(得分:2)

如果输入的数字确实是N !,则计算N相当简单。

由于大整数算法的开销,天真的方法计算阶乘将太慢。相反,我们可以注意到,当N≥7时,每个阶乘可以通过其长度(即位数)来唯一标识。

  • 整数x的长度可以计算为log10(x)+ 1。
  • 对数的乘积规则:log(a * b)= log(a)+ log(b)

通过使用以上两个事实,我们可以说N的长度!是:

enter image description here

可以通过简单地将log10(i)加到输入数字的长度来计算,因为log(1 * 2 * 3 * ... * n)= log(1)+ log(2)+ log (3)+ ... + log(n)。

此C ++代码应该可以解决问题:

double result = 0;
for (int i = 1; i <= 1000000; ++i) {  // This should work for 1000000! (where inputNumber has 10^7 digits)
    result += log10(i);
    if ( (int)result + 1 == inputNumber.size() ) {    // assuming inputNumber is a string of N!
        std::cout << i << endl;
        break;
    }
}

(记住检查n <7的情况(在这里可以进行基本阶乘计算))

完整代码:https://pastebin.com/9EVP7uJM

答案 7 :(得分:2)

这是一些clojure代码:

(defn- reverse-fact-help [n div]
    (cond (not (= 0 (rem n div))) nil
          (= 1 (quot n div)) div
          :else (reverse-fact-help (/ n div) (+ div 1))))
(defn reverse-fact [n] (reverse-fact-help n 2))

假设n = 120,div = 2。 120/2 = 60,60 / 3 = 20,20 / 4 = 5,5 / 5 = 1,返回5

假设n = 12,div = 2。 12/2 = 6,6 / 3 = 2,2 / 4 = .5,返回'nil'

答案 8 :(得分:1)

inverse_factorial( X )
{
   X_LOCAL = X;
   ANSWER = 1;
   while(1){
      if(X_LOCAL / ANSWER == 1)
        return ANSWER;
       X_LOCAL = X_LOCAL / ANSWER;
       ANSWER = ANSWER + 1;
    }
}

答案 9 :(得分:1)

此功能基于逐次逼近!我创建了它并在Advanced Trigonometry Calculator 1.7.0

中实现了它
double arcfact(double f){
 double result=0,precision=1000;
 int i=0;
 if(f>0){
   while(precision>1E-309){
     while(f>fact(result+precision)&&i<10){
 result=result+precision;
 i++;
   }
   precision=precision/10;
   i=0;
  }
  }
  else{
result=0;
   }
   return result;
 }

答案 10 :(得分:1)

int p = 1,i;
//assume variable fact_n has the value n!
for(i = 2; p <= fact_n; i++) p = p*i;
//i is the number you are looking for if p == fact_n else fact_n is not a factorial

我知道它不是伪代码,但它很容易理解

答案 11 :(得分:0)

来自我的应用程序Advanced Trigonometry Calculator v1.6.8

    double arcfact(double f) {
        double i=1,result=f;
        while((result/(i+1))>=1) {
            result=result/i;
            i++;
        }
        return result;
    }

你怎么看?适用于阶乘整数。

答案 12 :(得分:0)

如果不知道数字M是否为N!,那么一个不错的测试是测试它是否可被所有小素数整除,直到Sterling逼近该素数大于M。或者,如果你有一个阶乘表,但它不够高,你可以选择表中最大的阶乘,并确保M可被整除。

答案 13 :(得分:0)

what the factorial的C / C ++代码(r是结果因子):

int wtf(int r) {
    int f = 1;

    while (r > 1)
        r /= ++f;

    return f;
}

样本测试:

Call: wtf(1)
Output: 1

Call: wtf(120)
Output: 5

Call: wtf(3628800)
Output: 10

答案 14 :(得分:0)

Simply divide by positive numbers, i.e: 5!=120 ->> 120/2 = 60 || 60/3 = 20 || 20/4 = 5 || 5/5 = 1

So the last number before result = 1 is your number.

In code you could do the following:

number = res
for x=2;res==x;x++{
    res = res/x

} 

or something like that. This calculation needs improvement for non-exact numbers.

答案 15 :(得分:0)

Most numbers are not in the range of outputs of the factorial function. If that is what you want to test, it's easy to get an approximation using Stirling's formula or the number of digits of the target number, as others have mentioned, then perform a binary search to determine factorials above and below the given number.

What is more interesting is constructing the inverse of the Gamma function, which extends the factorial function to positive real numbers (and to most complex numbers, too). It turns out construction of an inverse is a difficult problem. However, it was solved explicitly for most positive real numbers in 2012 in the following paper: http://www.ams.org/journals/proc/2012-140-04/S0002-9939-2011-11023-2/S0002-9939-2011-11023-2.pdf . The explicit formula is given in Corollary 6 at the end of the paper.

Note that it involves an integral on an infinite domain, but with a careful analysis I believe a reasonable implementation could be constructed. Whether that is better than a simple successive approximation scheme in practice, I don't know.

答案 16 :(得分:0)

基于:

Full inverted factorial valid for x>1

使用建议的计算。如果factorial以完全二进制形式表示,则算法为:

假设输入是阶乘x,x = n!

  1. 返回1 for 1
  2. 找出阶乘x的二进制扩展中的尾随0的数量,让我们用t
  3. 标记它
  4. 计算x / fact(t),x除以t的阶乘,数学上x /(t!)
  5. 查找x / fact(t)除以t + 1的次数,向下舍入到最接近的整数,让我们用m标记它
  6. 返回m + t
  7. __uint128_t  factorial(int  n);
    
    int invert_factorial(__uint128_t fact)
    {
        if (fact == 1) return 1;
    
        int t = __builtin_ffs(fact)-1;
        int res = fact/factorial(t);
    
        return t + (int)log(res)/log(t+1);
    }
    

    128位在34上给予!