查找十进制数字总和的最快方法

时间:2012-01-23 05:34:22

标签: c++ algorithm math

查找小数位总和的最快方法是什么? 以下代码是我写的,但范围1 to 1000000000000000000

的速度非常慢
long long sum_of_digits(long long input) {
    long long total = 0;
    while (input != 0) {
        total += input % 10;
        input /= 10;
    }
    return total;
}

int main ( int argc, char** argv) {
    for ( long long i = 1L; i <= 1000000000000000000L; i++) {
        sum_of_digits(i);
    }
    return 0;
}

14 个答案:

答案 0 :(得分:5)

我假设你要做的是

#include <iostream>
const long long limit = 1000000000000000000LL;
int main () {
   long long grand_total = 0;
   for (long long ii = 1; ii <= limit; ++ii) {
      grand_total += sum_of_digits(i);
   }
   std::cout << "Grand total = " << grand_total << "\n";
   return 0;
}

这不会有两个原因:

  • 这需要很长时间。
  • 它会溢出。

要处理溢出问题,您必须对上限设置限制或使用一些bignum包。我会解决这个问题。

为了应对创造性所需的计算负担。如果你知道上限限制为10的幂,这相当容易。如果上限可以是任意数字,则必须更具创造性。

首先看一下计算从0到10 n -1的所有整数的位数之和的问题(例如,0到9(n = 1),0到99(n = 2) )),等于10 n -1的所有整数的位数之和为S n 。对于n = 1(0到9),这仅是0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45(9 * 10/2)。因此S 1 = 45。

对于n = 2(0到99),你总计0-9十次,你再次总结0-9十次。对于n = 3(0到999),您将总计0-99十次,并且您将0-9 100次相加。对于n = 4(0到9999),您将总计0-999十次,并且您将0-9 1000次相加。通常,S n = 10S n-1 +10 n-1 S 1 作为递归表达式。这简化为S n =(9n10 n )/ 2.

如果上限是10 n 的形式,则解决方案是上面的S n 加上一个数字1000 ... 000。如果上限是任意数字,您将需要再次创作。仔细考虑开发S n 的公式。

答案 1 :(得分:2)

阅读你的编辑:在1到1000000000000000000之间的循环中计算该函数需要很长时间。这是一个没脑子的人。

100亿亿是100亿。您的处理器每秒最多可以完成数十亿次操作。即使使用不存在的4-5 Ghz处理器,并假设最好的情况下它编译为一个加法,一个mod,一个div和一个比较跳转,你每秒只能进行10亿次迭代,这意味着它将采取的顺序为10亿秒。

答案 2 :(得分:2)

你可以递归地解决这个问题。 18位数字的总和是前9位数加上后9位数的总和。同样,9位数的位数之和将是前4位或5位的总和加上最后5位或4位的总和。当然,当值为0时,您可以使用特殊情况。

答案 3 :(得分:2)

您可能不希望以强力方式执行此操作。这似乎更像是一个逻辑思维问题。

注 - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = N(N + 1)/ 2 = 45。

----改变答案,使大卫的评论后更清楚

见大卫的答案 - 我错了

答案 4 :(得分:1)

我认为你不能比O(N) N is the number of digits in the given number(这在计算上并不昂贵)做得更好

但是,如果我正确理解您的问题(范围),您想输出一系列数字的数字总和。在这种情况下,当您从number0转到number9然后减少8时,您可以增加1。

答案 5 :(得分:1)

你需要作弊 - 寻找能让你缩短计算时间的数学模式。

  • 例如,你真的需要测试那个输入!每次都是= 0?如果多次添加0/10是否重要?由于它无关紧要,请考虑展开循环。
  • 您可以在更大的基础上进行计算,例如,基数10 ^ 2,10 ^ 3等,这可能会让您减少数字位数,然后您必须将其转换回基数10?如果这样做,您将能够更轻松地实现缓存。
  • 考虑一下编译器内在函数,它可以让你给编译器提供分支预测的提示。
  • 鉴于这是C ++,请考虑使用模板元编程实现这一点。
  • 鉴于sum_of_digits纯粹是功能性的,请考虑缓存结果。

现在,大多数这些建议都会适得其反 - 但我要说的是,如果你已达到计算机对给定算法的限制,你需要找到一个不同的解决方案。 / p>

如果您想详细调查一下,这可能是一个很好的起点:http://mathworld.wolfram.com/DigitSum.html

答案 6 :(得分:1)

可能性1:

您可以通过将循环的一次迭代结果输入下一次迭代来加快速度。

例如,如果i == 365,则结果为14。在下一个循环中,i == 366 - 比前一个结果多1个。总和还是1:3 + 6 + 6 = 15

存在进位数时出现问题。如果i == 99(即结果= 18),则下一个循环的结果不是19,它是1.您将需要额外的代码来检测这种情况。

可能性2:

虽然考虑到上述情况,但在我看来,sum_of_digits绘制时的结果序列将类似于锯齿。通过对结果图的一些分析(我将其留作读者的练习),可以确定一种方法,以允许直接计算总和结果。

然而,正如其他一些人所指出的那样:即使尽可能快地实现sum_of_digits和最优化的循环代码,你也不可能在任何有用的时间范围内计算1000000000000000000结果,当然也不能少于一秒钟。

答案 7 :(得分:1)

聚会很晚,但无论如何,这是我的解决方案。对不起,这是用Python而不是C ++,但它应该相对容易翻译。而且因为这主要是一个算法问题,我希望没问题。

至于溢出问题,唯一想到的是使用数字数组而不是实际数字。鉴于此算法,我希望它不会对性能产生太大影响。

https://gist.github.com/frnhr/7608873

它使用了我通过观察和探究问题找到的这三个递归。而不是尝试提出一些通用和神秘的方程式,这里有三个例子。一般情况应该很容易看出来。

关系1

使用任意参数将函数调用减少到具有更多可预测参数的几个递归调用,以便在关系2和3中使用。

foo(3456) == foo(3000)
           + foo(400) + 400 * (3)
           + foo(50) + 50 * (3 + 4)
           + foo(6) + 6 * (3 + 4 + 5)

关系2

使用L*10^M形式的参数(例如:30,7000,900000)将调用减少为可用于关系3的递归调用。这些triangular numbers在不请自来的情况下弹出(但欢迎):)< / p>

triangular_numbers = [0, 1, 3, 6, 10, 15, 21, 28, 36]  # 0 not used
foo(3000) == 3 * foo(1000) + triangular_numbers[3 - 1] * 1000

仅在L > 1时有用。它适用于L = 1,但是微不足道。在这种情况下,直接转到关系3。

关系3

使用格式为1*10^M的参数递归减少调用,并将参数除以10。

foo(1000) == foo(100) * 10 + 44 * 100 + 100 - 9  # 44 and 9 are constants

最终,你只需要真正计算数字0到10的总和数,结果证明只需要最多3个计算。这个递归会处理其他所有事情。我很确定它会在O(logN)时间运行。那是FAAST !!!!! 11one

在我的笔记本电脑上,它计算出给定数字的数字总和,在7秒内超过1300位!您的测试(1000000000000000000)计算在0.000112057秒!

答案 8 :(得分:0)

编辑:似乎你想要实际数字的总和,使得:12345 = 1 + 2 + 3 + 4 + 5不是数字的数量,也不是所有数字1到12345(包括)的总和; < / p>

因此,您可以获得的最快速度是:

long long sum_of_digits(long long input) {
    long long total = input % 10;
    while ((input /= 10) != 0)
        total += input % 10;
    return total;
}

当你运行足够的迭代时,这仍然会很慢。您对1,000,000,000,000,000,000L迭代的要求是百万,百万,百万。鉴于1亿美元在我的计算机上需要大约10,000毫秒,人们可以预期每100万条记录需要100毫秒,而你想要再花费100万次。一天只有86400秒,所以我们每天最多可以计算出86,400万条记录。这需要一台电脑

让我们假设您的方法可以在单个浮点运算中执行(不知何故),假设您正在使用K计算机,这是目前最快的(Rmax)超级计算机,超过10 petaflops,如果你做的数学是= 10,000万每秒百万次浮动操作。这意味着您的100万,百万,百万循环将采用世界上最快的非分布式超级计算机100秒来计算总和(如果需要1次浮点运算来计算,它不能),所以你需要等待很长一段时间,计算机变得更加强大,使你的解决方案可以在一秒钟内运行。

你要做的事情是,你要么近乎实时地做一个无法解决的问题(例如:与图形计算有关),要么你误解了给你的问题/任务,或者你是预期的执行比任何(非分布式)计算机系统更快的事情。

如果您的任务实际上是在显示一个范围的所有数字并将其输出,那么答案就不是改进for循环。例如:

1 = 0
10 = 46
100 = 901
1000 = 13501
10000 = 180001
100000 = 2250001
1000000 = 27000001
10000000 = 315000001
100000000 = 3600000001

从这里你可以计算出一个公式来实际计算从1到N的所有数字的所有数字的总和。但是,除了速度更快的计算机之外,还不清楚你真正想要的是什么。

答案 9 :(得分:0)

不是最好的,但很简单:

int DigitSumRange(int a, int b) {
    int s = 0;
    for (; a <= b; a++)
        for(c : to_string(a)) 
            s += c-48;

    return s;
}

答案 10 :(得分:0)

下面给出了一个 Python 函数,它将数字转换为字符串,然后转换为数字列表,然后找到这些数字的总和。

def SumDigits(n):
    ns=list(str(n))
    z=[int(d) for d in ns]
    return(sum(z))

答案 11 :(得分:0)

在 C++ 中,最快的方法之一是使用字符串。 首先从字符串中获取用户的输入。然后将字符串的每个元素转换为int后添加。可以使用 -> (str[i] - '0').

    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {   string str;
        cin>>str;
        long long int sum=0;
        for(long long int i=0;i<str.length();i++){
          sum =  sum + (str[i]-'0');
      }
        cout<<sum;
    }
    
          

答案 12 :(得分:-3)

如果您想找到范围为1到N的总和,那么只需执行以下操作

long sum = N(N+1)/2;

这是最快的方式。

答案 13 :(得分:-3)

查找1到N之间数字总和的公式为:

(1 + N)*(N / 2)

http://mathforum.org/library/drmath/view/57919.html

有一个用C#编写的类,它支持一个超过支持的最大长度限制的数字。 你可以在这里找到它。 Oyster.Math

使用这个类,我在c#中生成了一段代码,可能对你有所帮助。

using Oyster.Math;
class Program
{
    private static DateTime startDate;
    static void Main(string[] args)
    {
        startDate = DateTime.Now;
        Console.WriteLine("Finding Sum of digits from {0} to {1}", 1L, 1000000000000000000L);
        sum_of_digits(1000000000000000000L);
        Console.WriteLine("Time Taken for the process: {0},", DateTime.Now - startDate);
        Console.ReadLine();
    }

    private static void sum_of_digits(long input)
    {
       var answer = IntX.Multiply(IntX.Parse(Convert.ToString(1 + input)), IntX.Parse(Convert.ToString(input / 2)), MultiplyMode.Classic);
        Console.WriteLine("Sum: {0}", answer);
    }
}

如果与您的背景无关,请忽略此评论。