如何有效地将数字中的每个数字相乘

时间:2013-06-28 04:47:47

标签: math language-agnostic

我希望将数字中的每个数字相乘。

例如

515 would become 25(i.e 5*1*5)
10 would become 0(i.e 1*0)
111111 would become 1(i.e 1*1*1*1*1*1)

我用这段代码做了

public static int evalulate(int no)
{
    if(no==0)return 0;
    int temp=1;

    do
    {
        temp=(no%10)*temp;
        no=no/10;
    }while(no>0);

    return temp;
}

问题是我想评估一下十亿这样的数字

for(int i=0;i<1000000000;i++)evaluate(i);

我的处理器需要 146 秒。我想在一些秒内评估它。

那么,是否可以使用一些shiftandor运算符来优化此代码,这样我就可以减少评估的时间,而无需使用多个线程或并行化它

由于

3 个答案:

答案 0 :(得分:8)

首先,弄清楚你可以在内存中存储多少个数字。对于这个例子,假设您可以存储999个数字。

您的第一步是预先计算0-999之间所有数字的数字乘积,并将其存储在内存中。所以,你有一个阵列:

  multLookup = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                0, 2, 4, 6, 8, 10, 12, 14, 16, 18,
                0, 3, 6, 9, 12, 15, 18, 21, 24, 27,
                0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
                ...]

现在,你将你的数字分成一堆3位数字。例如,如果您的号码为1739203423,则会将其细分为1739203423。您可以在multLookup数组中查看每个结果,并将结果相乘,如下所示:

  solution = multLookup[1] * multLookup[739] * multLookup[203] * multLookup[423];

通过这种方法,您可以将计算加速3倍(因为我们选择了999个项目存储在内存中)。要将速度提高5,请在内存中存储99999个数字,然后按照相同的步骤操作。在您的情况下,将其加速5意味着您将在 29.2秒中到达您的解决方案。

注意:相对于存储在内存中的数量,增益并不完全是线性的。在这个答案的评论中看到jogojapan的推理原因是什么。

如果您更了解数字显示的顺序或数字的范围(例如您的输入仅在[0,10000]范围内),则可以使此算法更智能。

在你的例子中,你使用for循环从0迭代到1000000000.在这种情况下,这种方法将是超级高效的,因为内存不会非常频繁地页面错误,并且将有更少的缓存未命中。

但等等! 您可以更快地实现这一点(针对您的特定for循环迭代示例)!怎么样,你问?缓存!让我们说你要经历10位数字。

假设您从8934236000开始。根据内存解决方案中的999位数字,您可以将其细分为8934236000。然后你会成倍增加:

solution = multLookup[8] * multLookup[934] * multLookup[236] * multLookup[0];

接下来,您需要8934236001,将其细分为8934236001,并乘以:

solution = multLookup[8] * multLookup[934] * multLookup[236] * multLookup[1];

依旧......但我们注意到前三次查找对于接下来的997次迭代是相同的!所以,我们缓存它。

cache = multLookup[8] * multLookup[934] * multLookup[236];

然后我们使用缓存:

for (int i = 0; i < 1000; i++) {
    solution = cache * i;
}

就这样,我们几乎把时间减少了4倍。所以你采用你所拥有的~29.2秒解决方案,并将其除以4,以便在 ~7.3秒内完成所有十亿个数字

答案 1 :(得分:6)

如果,您可以存储所有号码的每项操作的结果。然后您可以使用Memoization。这样你只需要计算1位数。

int prodOf(int num){
   // can be optimized to store 1/10 of the numbers, since the last digit will always be processed
   static std::vector<int> memo(<max number of iterations>, -1); 
   if(num == 0) return 0;

   if(memo[num] != -1 )return memo[num];

   int prod = (num%10)  * prodOf(num/10);

   memo[num] = prod;

   return prod;
}

答案 2 :(得分:1)

我做了一些测试, 在我的PC上使用简单的C / C ++代码(Xeon 3.2GHz),

  

姓否= i = 999999999 ==&gt; 387420489 nb sec 23

#include "stdafx.h"
#include <chrono>
#include <iostream>

#undef _TRACE_

inline int evaluate(int no)
{
#ifdef _TRACE_
    std::cout << no;
#endif

    if(no==0)return 0;
    int temp=1;

    do
    {
        temp=(no%10)*temp;
        no=no/10;
    }while(no>0);
#ifdef _TRACE_
    std::cout << " => " <<  temp << std::endl;
#endif // _TRACE_
    return temp;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::time_point<std::chrono::system_clock> start(std::chrono::system_clock::now());
    int last = 0;
    int i = 0;
    for(/*int i = 0*/;i<1000000000;++i) {
        last = evaluate(i);
    }
    std::cout << "last no = i = " << (i-1) << " ==> " << last << std::endl;
    std::chrono::time_point<std::chrono::system_clock> end(std::chrono::system_clock::now());
    std::cout << "nb sec " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << std::endl;

    return 0;
}

我还用openMP测试了多个线程的循环拆分,结果为0秒, 所以我想说如果你考虑使用真正有效的语言的性能问题会很有用。

pragma omp parallel for 
for(int i = 0;i<1000000000;++i) {
     /*last[threadID][i] = */evaluate(i);
}