Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 阶乘的位数之和_Algorithm_Dynamic Programming_Sum Of Digits - Fatal编程技术网

Algorithm 阶乘的位数之和

Algorithm 阶乘的位数之和,algorithm,dynamic-programming,sum-of-digits,Algorithm,Dynamic Programming,Sum Of Digits,这不是一个家庭作业问题。我只是想有人可能知道这个问题的真正解决办法 2004年我参加了一个编程竞赛,当时有一个问题: 给定n,求n!的位数之和!。n可以是0到10000。时限:1秒。我认为每个测试集最多有100个数字 我的解决方案相当快,但不够快,所以我让它运行了一段时间。它构建了一个预先计算的值数组,我可以在代码中使用这些值。这是一次黑客攻击,但成功了 但是有一个家伙,他用大约10行代码解决了这个问题,它很快就会给出答案。我相信这是某种动态规划,或是数论的东西。那时我们16岁,所以这不应该是一

这不是一个家庭作业问题。我只是想有人可能知道这个问题的真正解决办法

2004年我参加了一个编程竞赛,当时有一个问题:

给定n,求n!的位数之和!。n可以是0到10000。时限:1秒。我认为每个测试集最多有100个数字

我的解决方案相当快,但不够快,所以我让它运行了一段时间。它构建了一个预先计算的值数组,我可以在代码中使用这些值。这是一次黑客攻击,但成功了

但是有一个家伙,他用大约10行代码解决了这个问题,它很快就会给出答案。我相信这是某种动态规划,或是数论的东西。那时我们16岁,所以这不应该是一门“火箭科学”

有人知道他可以使用什么样的算法吗

编辑:如果我没有把问题说清楚,我很抱歉。正如mquander所说,应该有一个聪明的解决方案,没有bugnum,只有简单的Pascal代码、几个循环、O(n2)或类似的东西。1秒不再是一种限制

我发现如果n>5,那么9除以阶乘的位数之和。我们还可以找到数字末尾有多少个零。我们能用这个吗


好的,另一个问题来自俄罗斯的编程竞赛。给予1秒1秒?为什么你不能计算n!把数字加起来?这是10000次乘法,不超过一万次加法,大约需要1千分之一秒。

小型、快速的python脚本,可在中找到。这是优雅的,但仍然蛮力

import sys
for arg in sys.argv[1:]:
    print reduce( lambda x,y: int(x)+int(y), 
          str( reduce( lambda x, y: x*y, range(1,int(arg)))))


这是我的房间。不幸的是,它没有任何关于如何有效计算它的有用提示——它的maple和mathematica配方采用了天真的方法。

假设你有一个大的数字(这是你的问题中最小的,假设N真的很大,而不是10000),让我们从那里继续


下面的技巧是因子N!通过分解所有n我要解决第二个问题,计算n!mod(N+1),使用。这就减少了测试N是否为素数的问题。

让我们看看。我们知道n的计算!因为任何一个合理的大数字最终都会导致一个带有大量尾随零的数字,而这些尾随零并不构成总和。一路上把零去掉怎么样?这样可以使数字的大小保持小一点


嗯,没有。我刚刚检查过,整数溢出仍然是一个大问题,即使在那时…

您必须计算致命数

1*2*3*4*5=120

如果只想计算数字之和,可以忽略结尾的零

六个人!你可以做12x6=72而不是120*6

七个人!您可以使用(72*7)MOD 10

编辑

我写回复太快了

10是两个素数2和5的结果

每次你有这两个因素,你可以忽略它们

1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15...

1   2   3   2   5   2   7   2   3    2   11    2   13    2    3
            2       3       2   3    5         2         7    5
                            2                  3
系数5出现在5、10、15…
然后,在乘以5、10、15之后,将出现一个结束零

我们有很多2和3。。。我们很快就会满员:-(

然后,你仍然需要一个大数字库


我应该被否决!

即使没有任意精度的整数,这也应该是可以强制执行的。在您链接到的问题陈述中,需要计算的最大阶乘是1000!。这是一个大约2500位的数字。所以只需执行以下操作:

  • 分配一个3000字节的数组,每个字节代表阶乘中的一个数字。从值1开始
  • 在数组上重复运行小学乘法,以计算阶乘
  • 对数字求和
  • 重复乘法是唯一可能缓慢的步骤,但我确信1000次乘法可以在一秒钟内完成,这是最糟糕的情况。如果不是,您可以提前计算一些“里程碑”值,然后将它们粘贴到您的程序中

    一种可能的优化方法是:在数组中出现尾随的零时,消除它们。它们不会影响答案


    显而易见:我在这里采用的是编程竞赛的方式。在专业工作中,你可能永远不会这样做。

    我不确定谁还在关注这条线索,但不管怎样,还是这样

    首先,在官方外观的链接版本中,它只需要1000个阶乘,而不是10000个阶乘。此外,当这个问题在另一个编程竞赛中被重用时,时间限制是3秒,而不是1秒。这使得你需要付出多大的努力才能得到足够快的解决方案

    第二,对于竞赛的实际参数,Peter的解决方案是合理的,但是如果使用32位体系结构,您可以将其速度提高5倍(如果只需要1000,甚至可以提高6倍)也就是说,不用处理单个数字,而是以100000为基数实现乘法。然后在最后,将每个超级数字中的数字相加。我不知道你在比赛中被允许使用的计算机有多好,但我在家里有一台台式机,大约和比赛一样旧。下面的示例代码1000!a需要16毫秒nd 2.15秒10000次!代码也会在0出现时忽略尾随的0,但这只节省了大约7%的工作量

    #include <stdio.h>
    int main() {
        unsigned int dig[10000], first=0, last=0, carry, n, x, sum=0;
        dig[0] = 1;    
        for(n=2; n <= 9999; n++) {
            carry = 0;
            for(x=first; x <= last; x++) {
                carry = dig[x]*n + carry;
                dig[x] = carry%100000;
                if(x == first && !(carry%100000)) first++;
                carry /= 100000; }
            if(carry) dig[++last] = carry; }
        for(x=first; x <= last; x++)
            sum += dig[x]%10 + (dig[x]/10)%10 + (dig[x]/100)%10 + (dig[x]/1000)%10
                + (dig[x]/10000)%10;
        printf("Sum: %d\n",sum); }
    
    然后你用Karatsuba做一个大的乘法运算

    甚至比Karatsuba更好的是基于傅里叶变换的Schonhage-Strassen乘法算法。碰巧,这两种算法都是现代大数库的一部分。快速计算大阶乘对于某些纯数学应用可能很重要。我认为Schonhage-Strassen在编程竞赛中太过份了。Karatsuba非常简单,你可以想象它是一个A+的问题解决方案


    提出的部分问题是一些猜测,即存在一个简单的数论
    1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15...
    
    1   2   3   2   5   2   7   2   3    2   11    2   13    2    3
                2       3       2   3    5         2         7    5
                                2                  3
    
    #include <stdio.h>
    int main() {
        unsigned int dig[10000], first=0, last=0, carry, n, x, sum=0;
        dig[0] = 1;    
        for(n=2; n <= 9999; n++) {
            carry = 0;
            for(x=first; x <= last; x++) {
                carry = dig[x]*n + carry;
                dig[x] = carry%100000;
                if(x == first && !(carry%100000)) first++;
                carry /= 100000; }
            if(carry) dig[++last] = carry; }
        for(x=first; x <= last; x++)
            sum += dig[x]%10 + (dig[x]/10)%10 + (dig[x]/100)%10 + (dig[x]/1000)%10
                + (dig[x]/10000)%10;
        printf("Sum: %d\n",sum); }
    
    prod(k,n) = prod(k,floor((k+n)/2))*prod(floor((k+n)/2)+1,n)
    
     static long q20(){
        long sum = 0;
        String factorial = factorial(new BigInteger("100")).toString();
        for(int i=0;i<factorial.length();i++){
            sum += Long.parseLong(factorial.charAt(i)+"");
        }
        return sum;
    }
    static BigInteger factorial(BigInteger n){
        BigInteger one = new BigInteger("1");
        if(n.equals(one)) return one;
        return n.multiply(factorial(n.subtract(one)));
    }