Algorithm 逆阶乘
我们都知道,如果给定N,就很容易计算N!。但是反过来呢 N!是给定的,你将要找到N-这可能吗?我很好奇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
X=1
F=X代码>
X
为NX=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;pint-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;
}
}