我希望将数字中的每个数字相乘。
例如
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 秒。我想在一些秒内评估它。
那么,是否可以使用一些shift
,and
,or
运算符来优化此代码,这样我就可以减少评估的时间,而无需使用多个线程或并行化它
由于
答案 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
,则会将其细分为1
,739
,203
和423
。您可以在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位数字,您可以将其细分为8
,934
,236
和000
。然后你会成倍增加:
solution = multLookup[8] * multLookup[934] * multLookup[236] * multLookup[0];
接下来,您需要8934236001
,将其细分为8
,934
,236
和001
,并乘以:
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);
}