Algorithm 逆阶乘

Algorithm 逆阶乘,algorithm,math,Algorithm,Math,我们都知道,如果给定N,就很容易计算N!。但是反过来呢 N!是给定的,你将要找到N-这可能吗?我很好奇 设置X=1 生成F=X F=输入吗?如果是,则X为N 如果没有,则设置X=X+1,然后再次从#2开始 您可以使用先前的F结果来计算新的F(new F=new X*old F) 考虑到除法通常比乘法需要更长的时间,如果不是更快的话,它和反方向的速度一样快。一个给定的阶乘A保证将小于A的所有整数作为除A之外的因子,因此您将花费与计算正在运行的因子一样多的时间来分解这些整数。int p=1,i; i

我们都知道,如果给定N,就很容易计算N!。但是反过来呢

N!是给定的,你将要找到N-这可能吗?我很好奇

  • 设置
    X=1
  • 生成
    F=X
  • F=输入吗?如果是,则
    X
    为N
  • 如果没有,则设置
    X=X+1
    ,然后再次从#2开始
  • 您可以使用先前的
    F
    结果来计算新的
    F
    new F=new X*old F

    考虑到除法通常比乘法需要更长的时间,如果不是更快的话,它和反方向的速度一样快。一个给定的阶乘
    A
    保证将小于A的所有整数作为除A之外的因子,因此您将花费与计算正在运行的因子一样多的时间来分解这些整数。

    int p=1,i;
    
    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
    
    //假设变量事实n的值为n! 对于(i=2;p
    int-inverse\u-factorial(int-factorial){
    int电流=1;
    while(阶乘>当前){
    if(阶乘当前百分比){
    return-1;//不可整除
    }
    阶乘/=电流;
    ++电流;
    }
    如果(当前==阶乘){
    回流;
    }
    返回-1;
    }
    
    是。让我们调用您的输入x。对于较小的x值,您可以尝试n的所有值,看看n!=x。对于较大的x,您可以在n上进行二进制搜索,以找到正确的n(如果存在)。请注意,我们有n!≈ e^(n-ln-n)(这是),所以你大概知道去哪里找

    当然,问题是很少有数字是阶乘;因此,您的问题只对一小部分输入有意义。如果您的输入很小(例如,适合32位或64位整数),那么查找表将是最佳解决方案

    (当然,你可以考虑反转的更一般的问题。再次,二进制搜索可能是最好的方式,而不是分析的东西。我很高兴在这里显示错误。)< /P> 编辑:实际上,如果你不确定x是一个阶乘数,那么使用斯特林近似法或伽马函数进行二元搜索,在简单解上可能得不到那么多(或任何东西)。逆阶乘的增长速度比对数慢(这是因为阶乘是超指数的),你必须做任意精度的算术才能找到阶乘,并将这些数字相乘

    例如,请参阅Draco Ater的答案,了解一个想法(当扩展到任意精度的算术时)将适用于所有x。Dav的答案更简单,可能更快,因为乘法比除法快,这是最自然的算法……这个问题似乎是简单性的另一个胜利。:-)如果你知道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)
    因子足够了)。

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

    查找表显然是一个:

    for (i = 0; i < MAX; ++i)
        Lookup[i!] = i; // you can calculate i! incrementally in O(1)
    
    (i=0;i Lookup[i!]=i;//可以在O(1)中增量计算i
    例如,您可以使用C++/C#/Java来实现这一点,或者如果您使用C++/C#/Java,它们有自己的类似于容器的哈希表

    如果您必须多次这样做,并且每次都必须很快,那么这非常有用,但是您可以花一些时间构建此表

    二进制搜索:假设数字为
    m=(1+N!)/2
    m!
    大于
    N!
    ?如果是,将搜索范围缩小到1和
    m!
    ,否则将搜索范围缩小到
    m!+1和
    N!
    。递归应用此逻辑

    当然,这些数字可能很大,你可能会做很多不必要的操作。一个更好的办法是使用二进制搜索在1和<代码> SqRT(n!)<代码>之间搜索,或者尝试找到更好的近似,虽然这可能不容易。考虑研究


    线性搜索:在这种情况下可能是最好的。计算
    1*2*3*..*k
    ,直到乘积等于
    N!
    ,然后输出
    k
    如果你不知道一个数字
    M
    是否是
    N!
    的话,一个好的测试是测试它是否可以被所有小素数整除,直到英镑接近该素数的近似值大于
    M
    。或者,如果你有一个阶乘表,但它不够高,你可以选择表中最大的阶乘,并确保
    M
    可以被它整除。

    如果你有二进制的Q=N!则计算后面的零。称这个数字为J

    如果N是2K或2K+1,那么J等于2K减去2K二进制表示法中的1个数,因此反复添加1,直到添加的1个数等于结果中的1个数

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

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

    (很抱歉,它太小了,但我手边没有处理较大数字的工具。)

    有19个尾随零,即

    10011
    
    现在递增:

    1: 10100
    2: 10101
    3: 10110 bingo!
    
    所以N是22或23。我需要一个23的素数因子,我必须选择23(2K+1正好是素数,但我没有计划,也不需要)。所以23^1应该除以23,它不除以Q,所以

    N=22
    

    以下是一些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=0.5,从我的应用程序高级三角计算器v1.6.8中以C返回“nil”

        double arcfact(double f) {
            double i=1,result=f;
            while((result/(i+1))>=1) {
                result=result/i;
                i++;
            }
            return result;
        }
    
    Wh
    (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))
    
        double arcfact(double f) {
            double i=1,result=f;
            while((result/(i+1))>=1) {
                result=result/i;
                i++;
            }
            return result;
        }
    
    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;
     }
    
    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
    
    number = res
    for x=2;res==x;x++{
        res = res/x
    
    } 
    
    __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);
    }
    
    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;
        }
    }