这几天没事做的时候都会上projecteuler.net上面去做题,其中14题是这样的:
he following iterative sequence is defined for the set of positive integers:
n
n
/2 (n
is even)
n
3n
+ 1 (n
is odd)
Using the rule above and starting with 13, we generate the following sequence:
It can be seen that this sequence (starting at 13 and finishing at
1) contains 10 terms. Although it has not been proved yet (Collatz
Problem), it is thought that all starting numbers finish at 1.
Which starting number, under one million, produces the longest chain?
题目并不难理解,这个据说是著名的角谷猜想,现在要找到100万以下的数字中展开这个链最长的数字是多少。如果我一开始就直接按照题意来解答,这个题目花不了几分钟,直接暴力法。然而我却想的太多了,我猜想在计算这个链条长度的过程中会不会有很多数字会重复计算,如果加上缓存
以前计算的结果是否能节约比较多的时间?那么第一次解答如下:
<!---->#include
<
iostream
>
#include
<
map
>
#include
<
windows.h
>
using
namespace
std;
unsigned
long
produce_term(unsigned
long
n)
{
if
(n
&
1
)
return
3
*
n
+
1
;
else
return
n
>>
1
;
}
int
main()
{
map
<
unsigned
long
,
int
>
counters;
int
max_i
=
0
;
int
max_count
=
0
;
DWORD tick1,tickPassed;
tick1
=
GetTickCount();
for
(
int
i
=
1
;i
<
1000000
;i
++
)
{
int
sum
=
2
;
unsigned
long
term
=
i;
while
((term
=
produce_term(term))
!=
1
)
{
if
(counters[term]){
sum
+=
counters[term];
break
;
}
else
sum
+=
1
;
}
if
(sum
>
max_count)
{
max_i
=
i;
max_count
=
sum;
counters[i]
=
sum;
}
}
tickPassed
=
GetTickCount()
-
tick1;
cout
<<
tickPassed
<<
endl;
cout
<<
max_i
<<
endl
<<
max_count
<<
endl;
return
0
;
}
遗憾的是,这个版本跑了快13分钟,太让人难以接受了。那么是否能优化下?怎么优化?我的机器是双核的,跑这个单进程单线程的程序只利用了一半的CPU,那么能不能搞成两个线程
来计算?缓存需要在两个线程之间做同步,显然读的多,写的少,应该采用读写锁
。OK,第二个版本利用ACE的线程封装实现如下:
<!---->#include
<
iostream
>
#include
<
map
>
#include
"
ace/Thread_mutex.h
"
#include
"
ace/Synch.h
"
#include
"
ace/Thread_Manager.h
"
using
namespace
std;
class
ThreadSafeMap
{
public
:
ThreadSafeMap()
{
}
int
get
(unsigned
long
n)
{
ACE_READ_GUARD_RETURN(ACE_RW_Thread_Mutex,guard,mutex_,
0
);
return
counters_[n];
}
int
put(unsigned
long
key,
int
value)
{
ACE_WRITE_GUARD_RETURN(ACE_RW_Thread_Mutex,guard,mutex_,
-
1
);
counters_[key]
=
value;
return
0
;
}
private
:
map
<
unsigned
long
,
int
>
counters_;
ACE_RW_Thread_Mutex mutex_;
};
unsigned
long
produce_term(unsigned
long
n)
{
if
(n
&
1
)
return
3
*
n
+
1
;
else
return
n
>>
1
;
}
static
ThreadSafeMap counters;
ACE_THR_FUNC_RETURN run_svc (
void
*
arg)
{
int
max_i
=
0
;
int
max_count
=
0
;
for
(
int
i
=
500001
;i
<
1000000
;i
++
)
{
int
sum
=
2
;
unsigned
long
term
=
i;
while
((term
=
produce_term(term))
!=
1
)
{
if
(counters.
get
(term)){
sum
+=
counters.
get
(term);
break
;
}
else
sum
+=
1
;
}
if
(sum
>
max_count)
{
max_i
=
i;
max_count
=
sum;
counters.put(i,sum);
}
}
cout
<<
max_i
<<
endl
<<
max_count
<<
endl;
return
0
;
}
int
main(
int
ac,
char
*
argv[])
{
if
(ACE_Thread_Manager::instance ()
->
spawn (
//
Pointer to function entry point.
run_svc,
//
<run_svc> parameter.
NULL,
THR_DETACHED
|
THR_SCOPE_SYSTEM)
==
-
1
)
return
-
1
;
int
max_i
=
0
;
int
max_count
=
0
;
for
(
int
i
=
1
;i
<
500000
;i
++
)
{
int
sum
=
2
;
unsigned
long
term
=
i;
while
((term
=
produce_term(term))
!=
1
)
{
if
(counters.
get
(term)){
sum
+=
counters.
get
(term);
break
;
}
else
sum
+=
1
;
}
if
(sum
>
max_count)
{
max_i
=
i;
max_count
=
sum;
counters.put(i,sum);
}
}
cout
<<
max_i
<<
endl
<<
max_count
<<
endl;
return
ACE_Thread_Manager::instance ()
->
wait ();
}
将数据分成了两半,利用两个线程来计算,果然快了一点,快了多少呢?从13分钟减少到9分钟,CPU利用率也到了100%,内存占用也降低了一半,似乎成绩不错呀。正在沾沾自喜之际,突然想起,能不能简单地暴力破解,咱不搞缓存,不搞多线程,看看效果怎么样。那么第三个版本简单实现如下:
<!---->#include
<
iostream
>
using
namespace
std;
unsigned
long
produce_term(unsigned
long
n)
{
if
(n
&
1
)
return
3
*
n
+
1
;
else
return
n
>>
1
;
}
int
main()
{
int
max_i;
int
max_count
=
0
;
for
(
int
i
=
1
;i
<
1000000
;i
++
)
{
int
count
=
2
;
unsigned
long
term
=
i;
while
((term
=
produce_term(term))
>
1
)
count
+=
1
;
if
(count
>
max_count){
max_i
=
i;
max_count
=
count;
}
}
cout
<<
max_i
<<
endl
<<
max_count
<<
endl;
system(
"
pause
"
);
return
0
;
}
程序执行的结果让我惊掉了下巴,竟然只执行了1秒多,换成java也是一样。什么缓存、多线程,全抛到了九霄云外。
总结教训,想当然的性能估计是愚不可及的,想当然的优化是愚不可及的,简单直接才是美!
分享到:
相关推荐
网上的SQL优化的文章实在是很多,说实在的,我也曾经到处找这样的文章,什么不要使用IN了,什么OR了,什么AND了,很多很多,还有很多人拿出仅几S甚至几MS的时间差的例子来证明着什么(有点可笑),让许多人不知道其是...
华三无线优化配置指导书
创建可笑的快速Lexers徽标创建可笑的快速Lexers。 徽标有两个目标:使创建Lexer变得容易,因此您可以专注于更复杂的问题。 要使生成的Lexer比您用手编写的任何内容都要快。 为了实现这些目标,徽标:将所有令牌定义...
网上的SQL优化的文章实在是很多,说实在的,我也曾经到处找这样的文章,什么不要使用IN了,什么OR了,什么AND了,很多很多,还有很多人拿出仅几S甚至几MS的时间差的例子来证明着什么(有点可笑),让许多人不知道其是...
Crux这个名字在英语中有关键、核心之意,暗示了这些扩展对于优化Emacs工作流程的重要性。 Crux的扩展覆盖了编辑、导航、代码补全、项目管理等多个方面,旨在提高生产力,减少不必要的操作。下面我们将深入探讨其中...
它的设计使其外观和行为类似于标准系统控件,并进行了优化以确保对滚动性能的影响最小。特征允许对UIScrollView的整个内容高度进行细粒度的滚动。 通过Objective-C运行时和KVO直接与UIScrollView互操作。 与标准...
React Storefront框架...可笑的速度React Storefront付出了更多努力,以从所有可能的实际和用户感知的性能优化中挤出速度,包括: 动态数据的高速缓存命中率高服务器端渲染自动创建AMP 动态数据的预测性预取代码拆分缓
假如企业不管4C只是、一味地强4调4P理论,那就是在闭门造车,一定会制订出可笑的销售政策、可笑的产品、可笑的促销计划;假如企业只是、一味地站在消费者的角度进行4C的时候,来满意消费者的需求,企业的成本将会...
例如,它提供了更强大的性能优化,如回收视图(view recycling)机制,减少内存占用和提高滚动流畅性。同时,RecyclerView支持多种布局管理器,如线性布局(LinearLayoutManager)、网格布局(GridLayoutManager)和...
描述中的"可笑的"可能是在描述漫画内容的幽默性质,或者是在开发过程中遇到了一些令人啼笑皆非的问题。结合标签"JavaScript",我们可以推测这是一个使用JavaScript技术来创建的Web应用,可能是用于展示漫画、制作...
在描述中提到的"可笑的",虽然在中文里主要表达一种幽默或滑稽的感觉,但在IT语境下,这可能是对设计或交互性的形容,比如一个具有趣味性、引人发笑的漫画网站或应用。这可能涉及到如何通过HTML的特性,如CSS...
38. ridiculous(可笑的):批评某些科技观念或应用的荒谬之处。 39. absurd(荒唐的):形容不切实际或无意义的科技项目。 40. substitute(取代):新的科技可能替代旧的技术。 41. overcome difficulties(克服...