算法与笨办法对比实验
2008-10-21 20:59
232 查看
算法与笨办法对比实验
有这么一个数,当把它的最后一位(个位)挪到第一位的时候,得到的新数刚好是原来数的两倍。问这个数是多少?
——出自 1985 出版的一本小学5年级学生用的数学课外读物——《儿童数学世界》
这个问题看似简单,就是要找一个数出来,把这个数个位上的数字挪到最前
google_ad_client = "pub-6924533005275861";
google_ad_slot = "0030867594";
google_ad_width = 300;
google_ad_height = 250;
更多请点击这儿面去,例如 123 变成 312,12345变成51234。但是还要求得到的“新数”要是原来数的两倍。
简单的分析一下这条业务规则,不难得出下面的结论:
1.
取一个数作为“原数”;
2.
把“原数”个位上的数字挪到最前面,保存为一个“新数”;
3.
比较两个数字,如果“新数”是“原数”的两倍,则打印两个数并退出程序;
4.
如果不符合要求,则原数自加1并回到步骤2。
显然通过手工方式找到这个数是不太现实的,为了加快查找这个数的速度,让我们编写一段代码来提高工作效率。已经实现的代码如下:
1
#~ defined a method to move the last number to line-begin
2
3
def get_new_number(original_number)
4
5
6
7
#~ get last number
8
9
last_number = original_number%10
10
11
puts "the last_number is : "+last_number.to_s
12
13
14
15
#~ get the length of original_number
16
17
original_number_in_sting=original_number.to_s
18
19
puts "the length of original_number is : "+original_number_in_sting.length.to_s
20
21
22
23
#~ set the original_number = original_number/10
24
25
original_number = original_number/10
26
27
puts "the new original_number is : "+original_number.to_s
28
29
30
31
#~ move the last number to line-begin of original_number
32
33
for counter in 2..original_number_in_sting.length
34
35
last_number=last_number*10
36
37
end
38
39
puts "the new last_number is : " + last_number.to_s
40
41
42
43
#~ return the new number
44
45
return last_number+original_number
46
47
48
49
end
50
51
52
53
54
55
#~ initialization
56
57
#~ set the variable original_number = a number that the number >11
58
59
original_number = 1
60
61
puts "First: the original_number is : " + original_number.to_s
62
63
#~ set the variable new_number = get_new_number(number01)
64
65
new_number = get_new_number(original_number)
66
67
puts "First: the new_number is : "+new_number.to_s
68
69
#~ finished initialization
70
71
72
73
74
75
while original_number*2 != new_number
76
77
78
79
original_number = original_number + 1
80
81
puts "the original_number in loop is : "+original_number.to_s
82
83
84
85
new_number = get_new_number(original_number)
86
87
puts "the new_number in loop is : "+new_number.to_s
88
89
90
91
end
92
93
94
95
puts "We’ve got the number! It is : "+original_number.to_s
96
上面的代码是在 Ruby 1.8.4 下面调试通过的。这是典型的完全通过分析业务规则并忠实于业务规则而实现的一段代码,其中定义了一个方法专门来处理“把个位的数字挪到最前面生成一个新数”这件事情,其他部分就是不断地反复比较、尝试,直到找到我们所期望的那个数。这段代码也并不复杂,其中“#~ ”表示注释掉的内容,puts 表示打印信息在屏幕上。如果你有兴趣可以很容易的用其他语言改写。
如果说上面的分析和代码实现是使用了“业务视角”的话,下面我们再换个视角看看。
根据业务规则——有这么一个数,当把它的最后一位(个位)挪到第一位的时候,得到的新数刚好是原来数的两倍——我们可以知道,这个数至少是两位以上的,并且可以人为的分为两个部分——个位部分和其他部分,可以用一个等式来表示这条业务规则想表达的意思:
2*(10X+Y) = Y*10 (n-1) + X
让我们继续化简这个等式:
20X+2Y = Y*10 (n-1) + X
↓
19X = Y*10 (n-1) – 2Y
↓
19X = Y (10 (n-1) -2)
最终我们得到了下面这个等式
X = Y (10 (n-1) -2)/19
在上面的等式中,Y表示个位上的数字,X表示其余的部分,n表示这个数的位数,例如:对于123这个数,Y=3,X=12,n=3。我们可以知道X和Y一定都是正整数,另外,我们还可以知道Y一定是一个0-9之间的数字,所以我们只要求得X的值,就很容易的可以知道我们要找的那个数了。
最后一个关键,就是n的取值,但其实这个值我们是可以控制的,我们可以尝试着给n赋一个值,然后把10 (n-1) 作为一个值来处理。如果在一个既定的范围内找不到我们需要的数,我们可以继续加大n的取值。这样在我们的等式中就只剩下X这一个未知数了。
最终我们得到下面的代码:
1 n = 1
2
3 #~ 计算10 的100次方内是否有我们要找的数
4
5 for i in 1..100
6
7 n =n * 10
8
9
for y in 1..9
10
11 #~ 如果找到了我们需要的数,就打印出“原数”和“新数”
12
13
if (y*(n-2))%19==0 then
14
15 puts "the original number is : " + (10*((y*(n-2))/19)+y).to_s
16
17 puts "the new number is : " + (2*(10*((y*(n-2))/19)+y)).to_s
18
19 end
20
21 end
22
23 end
24
执行这段代码后,我们获得了下面这些返回结果:
the original number is
: 52631578947368421
the new number is
: 105263157894736842
the original number is
: 105263157894736842
the new number is
: 210526315789473684
the original number is
: 157894736842105263
the new number is
: 315789473684210526
the original number is
: 210526315789473684
the new number is
: 421052631578947368
the original number is
: 263157894736842105
the new number is
: 526315789473684210
the original number is
: 315789473684210526
the new number is
: 631578947368421052
the original number is
: 368421052631578947
the new number is
: 736842105263157894
the original number is
: 421052631578947368
the new number is
: 842105263157894736
the original number is
: 473684210526315789
the new number is
: 947368421052631578
从中我们可以看到,除了标为红色的第一组以外,其他的数都是符合我们要求的。
前面写了这么大堆,当然目的还是想说明一下这两种方法的差别。
第一种方法是完全面向业务的分析和实现方法,代码并不算累赘,而且很容易通过阅读代码反向来了解业务的原始需求和业务规则;而后一种则是通过数学的方法进行分析和抽象之后得到的结果。相比较之下,相信大家不能看出两者之间执行效率上的差距——因为第一种方法的原理是从1开始逐个尝试。
我就不再计算圈复杂度或者执行效率之类的刻板数据了,让我们用一个更直观的方法来对比一下这两种算法的差别。
第一种方法是我最开始的做法,那段代码看似中规中矩,但是通过最后得到的结果我们可以看到,符合我们要求的最小的一个数也大于10的17次方,而使用我的方法在一台P4 3G + 1G 内存的机器上运行了一小时也不过才尝试到10的9次方,照此计算,至少要连续运行10多万年才能找到第一个符合要求的数字。
而第二种方法,则是得益于QQ群里一位昵称为“岚”的朋友的指点,使用这个方法,一秒中已经可以完成10的100次方以内的查找。
1秒 vs. 10万年!
这就是算法的力量!这就是知识的价值!
如果你还在用代码描述着业务,那么尝试一下第二种方法吧 ^_^
再次感谢“岚”的指点。
我觉得这个不是算法的问题,是基本的数学问题。如果拿到一个数学问题不先用数学方法分析,而用穷举法解决,这个个人逻辑有问题。
有这么一个数,当把它的最后一位(个位)挪到第一位的时候,得到的新数刚好是原来数的两倍。问这个数是多少?
——出自 1985 出版的一本小学5年级学生用的数学课外读物——《儿童数学世界》
这个问题看似简单,就是要找一个数出来,把这个数个位上的数字挪到最前
google_ad_client = "pub-6924533005275861";
google_ad_slot = "0030867594";
google_ad_width = 300;
google_ad_height = 250;
更多请点击这儿面去,例如 123 变成 312,12345变成51234。但是还要求得到的“新数”要是原来数的两倍。
简单的分析一下这条业务规则,不难得出下面的结论:
1.
取一个数作为“原数”;
2.
把“原数”个位上的数字挪到最前面,保存为一个“新数”;
3.
比较两个数字,如果“新数”是“原数”的两倍,则打印两个数并退出程序;
4.
如果不符合要求,则原数自加1并回到步骤2。
显然通过手工方式找到这个数是不太现实的,为了加快查找这个数的速度,让我们编写一段代码来提高工作效率。已经实现的代码如下:
1
#~ defined a method to move the last number to line-begin
2
3
def get_new_number(original_number)
4
5
6
7
#~ get last number
8
9
last_number = original_number%10
10
11
puts "the last_number is : "+last_number.to_s
12
13
14
15
#~ get the length of original_number
16
17
original_number_in_sting=original_number.to_s
18
19
puts "the length of original_number is : "+original_number_in_sting.length.to_s
20
21
22
23
#~ set the original_number = original_number/10
24
25
original_number = original_number/10
26
27
puts "the new original_number is : "+original_number.to_s
28
29
30
31
#~ move the last number to line-begin of original_number
32
33
for counter in 2..original_number_in_sting.length
34
35
last_number=last_number*10
36
37
end
38
39
puts "the new last_number is : " + last_number.to_s
40
41
42
43
#~ return the new number
44
45
return last_number+original_number
46
47
48
49
end
50
51
52
53
54
55
#~ initialization
56
57
#~ set the variable original_number = a number that the number >11
58
59
original_number = 1
60
61
puts "First: the original_number is : " + original_number.to_s
62
63
#~ set the variable new_number = get_new_number(number01)
64
65
new_number = get_new_number(original_number)
66
67
puts "First: the new_number is : "+new_number.to_s
68
69
#~ finished initialization
70
71
72
73
74
75
while original_number*2 != new_number
76
77
78
79
original_number = original_number + 1
80
81
puts "the original_number in loop is : "+original_number.to_s
82
83
84
85
new_number = get_new_number(original_number)
86
87
puts "the new_number in loop is : "+new_number.to_s
88
89
90
91
end
92
93
94
95
puts "We’ve got the number! It is : "+original_number.to_s
96
上面的代码是在 Ruby 1.8.4 下面调试通过的。这是典型的完全通过分析业务规则并忠实于业务规则而实现的一段代码,其中定义了一个方法专门来处理“把个位的数字挪到最前面生成一个新数”这件事情,其他部分就是不断地反复比较、尝试,直到找到我们所期望的那个数。这段代码也并不复杂,其中“#~ ”表示注释掉的内容,puts 表示打印信息在屏幕上。如果你有兴趣可以很容易的用其他语言改写。
如果说上面的分析和代码实现是使用了“业务视角”的话,下面我们再换个视角看看。
根据业务规则——有这么一个数,当把它的最后一位(个位)挪到第一位的时候,得到的新数刚好是原来数的两倍——我们可以知道,这个数至少是两位以上的,并且可以人为的分为两个部分——个位部分和其他部分,可以用一个等式来表示这条业务规则想表达的意思:
2*(10X+Y) = Y*10 (n-1) + X
让我们继续化简这个等式:
20X+2Y = Y*10 (n-1) + X
↓
19X = Y*10 (n-1) – 2Y
↓
19X = Y (10 (n-1) -2)
最终我们得到了下面这个等式
X = Y (10 (n-1) -2)/19
在上面的等式中,Y表示个位上的数字,X表示其余的部分,n表示这个数的位数,例如:对于123这个数,Y=3,X=12,n=3。我们可以知道X和Y一定都是正整数,另外,我们还可以知道Y一定是一个0-9之间的数字,所以我们只要求得X的值,就很容易的可以知道我们要找的那个数了。
最后一个关键,就是n的取值,但其实这个值我们是可以控制的,我们可以尝试着给n赋一个值,然后把10 (n-1) 作为一个值来处理。如果在一个既定的范围内找不到我们需要的数,我们可以继续加大n的取值。这样在我们的等式中就只剩下X这一个未知数了。
最终我们得到下面的代码:
1 n = 1
2
3 #~ 计算10 的100次方内是否有我们要找的数
4
5 for i in 1..100
6
7 n =n * 10
8
9
for y in 1..9
10
11 #~ 如果找到了我们需要的数,就打印出“原数”和“新数”
12
13
if (y*(n-2))%19==0 then
14
15 puts "the original number is : " + (10*((y*(n-2))/19)+y).to_s
16
17 puts "the new number is : " + (2*(10*((y*(n-2))/19)+y)).to_s
18
19 end
20
21 end
22
23 end
24
执行这段代码后,我们获得了下面这些返回结果:
the original number is
: 52631578947368421
the new number is
: 105263157894736842
the original number is
: 105263157894736842
the new number is
: 210526315789473684
the original number is
: 157894736842105263
the new number is
: 315789473684210526
the original number is
: 210526315789473684
the new number is
: 421052631578947368
the original number is
: 263157894736842105
the new number is
: 526315789473684210
the original number is
: 315789473684210526
the new number is
: 631578947368421052
the original number is
: 368421052631578947
the new number is
: 736842105263157894
the original number is
: 421052631578947368
the new number is
: 842105263157894736
the original number is
: 473684210526315789
the new number is
: 947368421052631578
从中我们可以看到,除了标为红色的第一组以外,其他的数都是符合我们要求的。
前面写了这么大堆,当然目的还是想说明一下这两种方法的差别。
第一种方法是完全面向业务的分析和实现方法,代码并不算累赘,而且很容易通过阅读代码反向来了解业务的原始需求和业务规则;而后一种则是通过数学的方法进行分析和抽象之后得到的结果。相比较之下,相信大家不能看出两者之间执行效率上的差距——因为第一种方法的原理是从1开始逐个尝试。
我就不再计算圈复杂度或者执行效率之类的刻板数据了,让我们用一个更直观的方法来对比一下这两种算法的差别。
第一种方法是我最开始的做法,那段代码看似中规中矩,但是通过最后得到的结果我们可以看到,符合我们要求的最小的一个数也大于10的17次方,而使用我的方法在一台P4 3G + 1G 内存的机器上运行了一小时也不过才尝试到10的9次方,照此计算,至少要连续运行10多万年才能找到第一个符合要求的数字。
而第二种方法,则是得益于QQ群里一位昵称为“岚”的朋友的指点,使用这个方法,一秒中已经可以完成10的100次方以内的查找。
1秒 vs. 10万年!
这就是算法的力量!这就是知识的价值!
如果你还在用代码描述着业务,那么尝试一下第二种方法吧 ^_^
再次感谢“岚”的指点。
我觉得这个不是算法的问题,是基本的数学问题。如果拿到一个数学问题不先用数学方法分析,而用穷举法解决,这个个人逻辑有问题。
相关文章推荐
- 区域内的人数预测算法对比实验分析
- 几种字符串匹配算法性能简单实验对比
- 图形学实验之多边形填充算法
- 编译原理实验之语法分析(算符优先分析算法(C语言))
- MySQL连接算法的问答辨析并与Oracle简单对比
- 计算机图形学 - 实验6 - Cohen Sutherland裁剪算法
- 实验二 直线生成算法
- 算法导论实验四_分治法求平面上的最小点对
- 数据结构与算法实验题 9.2 材料
- 南邮算法分析和实验设计1 分而治之
- 文本压缩算法的对比和选择
- Java单例模式在多线程环境下的性能测试对比实验分析
- 重现unladen-swallow的性能对比实验
- javascript同源策略和跨域实验及其跨域解决办法
- Python机器学习库sklearn几种回归算法建模及分析(实验)
- HDU 3791 二叉搜索树 (数据结构与算法实验题 10.2 小明) BST
- 数据库性能对比测试实验
- 算法习题36:n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系
- 算法分析与设计实验 动态规划法 求最长公共子序列
- 三种强大的物体识别算法——SIFT/SURF、haar特征、广义hough变换的特性对比分析 .