void HappyTest()
{
for (int i = 0; i < 2100000000; i++) {
int n = i;
while (n >= 10) {
int m = n, sum = 0;
while (m != 0) {
int t = m >= 10 ? m % 10 : m;
sum += t * t;
m /= 10;
}
n = sum;
}
//return n == 1 || n == 7;
//if (i % 10000000 == 0) {
// cout << i << endl;
}
}
VS2017 Debug Mode Performance analyzer
我使用vs2017的性能分析工具获取图中的数据,发现性能消耗主要是%和*运算。 这部分代码中还有优化的空间吗?
答案 0 :(得分:0)
让我们先对代码进行较小的重写-例如:
int HappyTest()
{
int n;
for (int i = 0; i < 210000000; i++) { // Reduced by a factor 10 compared to OP code
n = i;
while (n >= 10) {
int m = n, sum = 0;
while (m != 0) {
int t = m >= 10 ? m % 10 : m;
sum += t * t;
m /= 10;
}
n = sum;
}
}
return n;
}
int main(void) {
printf("%d\n", HappyTest());
}
在我的系统上运行此程序需要10.7秒。
您的代码大约是:取一个数字,然后计算所有数字平方的总和。
因此您的乘法仅限于10个不同的乘法,即0 * 0、1 * 1,...,9 * 9
因此,一种优化的想法可能是将结果放入表中并进行表查找而不是乘法。喜欢:
int mul_tab_10[10] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81};
int HappyTest()
{
int n;
for (int i = 0; i < 210000000; i++) {
n = i;
while (n >= 10) {
int m = n, sum = 0;
while (m != 0) {
int t = m >= 10 ? m % 10 : m;
sum += mul_tab_10[t];
m /= 10;
}
n = sum;
}
}
return n;
}
在我的系统上运行此程序需要12.9秒。所以它慢。
但是,如果我们进一步迈出这一步怎么办?不仅可以使用包含10个元素的表,还可以使用包含100个元素的表。喜欢:
int mul_tab_100[100] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 4, 5, 8, 13, 20, 29, 40, 53, 68, 85, 9, 10, 13, 18, 25, 34, 45, 58, 73, 90, 16, 17, 20, 25, 32, 41, 52, 65, 80, 97, 25, 26, 29, 34, 41, 50, 61, 74, 89, 106, 36, 37, 40, 45, 52, 61, 72, 85, 100, 117, 49, 50, 53, 58, 65, 74, 85, 98, 113, 130, 64, 65, 68, 73, 80, 89, 100, 113, 128, 145, 81, 82, 85, 90, 97, 106, 117, 130, 145, 162};
int HappyTest()
{
int n;
for (int i = 0; i < 210000000; i++) {
n = i;
while (n >= 10) {
int m = n, sum = 0;
while (m != 0) {
int t = m >= 100 ? m % 100 : m; // Notice this change
sum += mul_tab_100[t];
m /= 100; // Notice this change
}
n = sum;
}
}
return n;
}
在我的系统上运行此程序需要6.9秒。因此,性能得到了改善。
再进行一步(即1000个元素)需要5.3秒,这又是改进。
是的,您可以提高运行时性能。
答案 1 :(得分:0)
是的,还有效率的余地,但是我不确定代码的实际作用,因为它不会寻找带有快乐数字的重复循环。但是:
摆脱不必要的int m
对于值<10 ,但没有 m % 10
除法,您避免了m / 10
模数。可以在循环外计算最后一位数字
使用缓存:您将在循环内一遍又一遍地检查相同的值(见下文)。
修改后的代码,尽管由于缺少花括号和注释的代码而导致不清楚
void HappyTest(void)
{
for (int i = 0; i < 2100000000; i++) {
int n = i;
while (n >= 10) {
int sum = 0;
while (n >= 10) { // while(m != 0)
int t = n % 10;
n /= 10;
sum += t * t;
}
sum += n * n; // final digit moved out of loop
n = sum;
}
if(n == 1 || n == 7) {
printf("%d\n", i);
}
}
}
9 * 9 * 10 = 810
(对于32位int
而言,实际上不能超过),该数字会反馈到第二次解析。因此可以对前810
个数字进行不同的处理-它们的结果存储在数组中。其余的数字只需对数字进行一个解析,然后就可以查询结果。
#define LIMIT 2100000000
#define CACHE_SZ 810
void HappyTest(void)
{
char cached[CACHE_SZ] = { 0 };
// the first part also sets up the cache
for (int i = 0; i < CACHE_SZ; i++) {
int n = i;
while (n >= 10) {
int sum = 0;
while (n >= 10) { // while(m != 0)
int t = n % 10;
n /= 10;
sum += t * t;
}
sum += n * n; // final digit moved out of loop
n = sum;
}
if(n == 1 || n == 7) {
//printf("%d\n", i);
cached[i] = 1;
results++;
}
}
// the second part continues more simply
for (int i = CACHE_SZ; i < LIMIT; i++) {
int n = i;
int sum = 0;
// only one parse of the number
while (n >= 10) {
int t = n % 10;
n /= 10;
sum += t * t;
}
sum += n * n;
// then look it up
if(cached[sum]) {
//printf("%d\n", i);
results++;
}
}
}
这大约需要您函数花费时间的1/3(两者都不含printf
)。