母亲说"冬"是节,所以今天下午后没有看书学习,跑去游戏机房狂玩Drifting。
回来后给读书的、考研的、工作的朋友们发个祝福。。
习惯性到CSDN看看贴子,发现有个标题很特别,叫"如何提高编程速度"
http://community.csdn.net/Expert/TopicView3.asp?id=5248357
区区没有回复这个贴子,因为这个问题本身有问题。。
<<<<<
楼主的post内容如下:
作 者: dirtysalt (李纳斯)
等 级:
信 誉 值: 100
所属论坛: C/C++ C语言
问题点数: 20
回复次数: 18
发表时间: 2006-12-22 9:35:59
我看了网上一位网友的帖子,做百度笔试题
求123456789字典顺序中一个特定的数
他们居然只用5分钟就想出来,花了20分钟解决
我,我居然花半个小时考虑,用了一个半小时才完全写正确
问题出来了,如何提高编程速度
<<<<<
一看又是百度的面试题,就知道都考的是算法了。题目没看明白。楼主表述不清,
还好,后来又补了一贴,区区才明白他问什么
<<<<<
回复人:dirtysalt(李纳斯) ( 一级(初级)) 信誉:100 2006-12-22 15:45:10 得分:0
我针对的并不是某个问题来问你们。只是当我看到同样的人做这道题目时,我有些感概。
具体题目是编写一个函数int getline(int n)
对于n=1 返回123456789
n=2返回123456798
n=3 123456879
以此类推
这个问题是百度一道面试题,我已经做完了,所以不需要劳烦大家。
只是看到别人花很少时间就写完,我有些感概。
我发现我的毛病,一些程序中小问题,导致调试时间很长,所以速度下降
我想知道你们是怎么解决这个问题的,如何提高编程速度(当然,按Jamesonang(Jameson Ang) 说就是要在好的算法下,尽快完成程序)。
<<<<<
题目的意思就是对一个字符串"123456789"来一个字典序全排列,然后按输入求出第N个排列。。
信手打开命令行,输入perl -e "print 9*8*7*6*5*4*3*2*1"
结果是 362880。。不知道百度的人对时间和空间复杂度要求是什么。这个问题规模不大。
所以先用脚本来写一个吧~
下面这个perl程序是随手写出来的,不考虑效率,在区区的机器上运行6秒左右可以算出所有的结果
#
perl
my @arr = 1 .. 9 ;
my @permute ;
sub make_permute{
my ( $head_str , $idx , $arr ) = @_ ;
if ( 8 == $idx ){
push @permute , $head_str . $arr -> [ 0 ];
return ;
}
for $num ( 0 .. @ $arr - 1 ){
( $arr -> [ $num ] , $arr -> [ 0 ]) = ( $arr -> [ 0 ] , $arr -> [ $num ]);
my $h = shift @ $arr ;
make_permute( $head_str . $h , $idx + 1 , $arr );
unshift @ $arr , $h ;
( $arr -> [ $num ] , $arr -> [ 0 ]) = ( $arr -> [ 0 ] , $arr -> [ $num ]);
}
}
make_permute( "" , 0 , @arr );
@permute = sort { $a <=> $b } ( @permute );
# my $FH;
#open $FH,">","./out.txt";
#print $FH "$_ " for (@permute); #打印到文件
#print "$_ " for (@permute); #打印到
####
my @arr = 1 .. 9 ;
my @permute ;
sub make_permute{
my ( $head_str , $idx , $arr ) = @_ ;
if ( 8 == $idx ){
push @permute , $head_str . $arr -> [ 0 ];
return ;
}
for $num ( 0 .. @ $arr - 1 ){
( $arr -> [ $num ] , $arr -> [ 0 ]) = ( $arr -> [ 0 ] , $arr -> [ $num ]);
my $h = shift @ $arr ;
make_permute( $head_str . $h , $idx + 1 , $arr );
unshift @ $arr , $h ;
( $arr -> [ $num ] , $arr -> [ 0 ]) = ( $arr -> [ 0 ] , $arr -> [ $num ]);
}
}
make_permute( "" , 0 , @arr );
@permute = sort { $a <=> $b } ( @permute );
# my $FH;
#open $FH,">","./out.txt";
#print $FH "$_ " for (@permute); #打印到文件
#print "$_ " for (@permute); #打印到
####
其实在思考题目时,这个perl程序并没有完全写出来,运行6秒应该也算是超时才对,
不过,这个脚本很容易实现,而且可以拿来当测试使用。。
如果对这个程序优化一下,速度可以提高很多的,第一个perl程序很烂,是因为最近没怎么
用perl,所以有些生疏。正常的perl黑客,应该可以不费吹灰之力写出下面的版本:
#
perl
my $top = 9 ;
my @arr = 1 .. $top ;
my @permute ;
sub make_permute{
my ( $head_str , $idx , $arr ) = @_ ;
if ( $top - 1 == $idx ){
push @permute , $head_str . $arr -> [ 0 ];
return ;
}
for $num ( 0 .. @ $arr - 1 ){
my @tarr = @ $arr ;
my $h = splice @tarr , $num , 1 ;
make_permute( $head_str . $h , $idx + 1 , @tarr );
}
}
make_permute( "" , 0 , @arr );
# ########################
my $top = 9 ;
my @arr = 1 .. $top ;
my @permute ;
sub make_permute{
my ( $head_str , $idx , $arr ) = @_ ;
if ( $top - 1 == $idx ){
push @permute , $head_str . $arr -> [ 0 ];
return ;
}
for $num ( 0 .. @ $arr - 1 ){
my @tarr = @ $arr ;
my $h = splice @tarr , $num , 1 ;
make_permute( $head_str . $h , $idx + 1 , @tarr );
}
}
make_permute( "" , 0 , @arr );
# ########################
改进过的程序可以对付小规模的问题,更易于调试。代码不但简洁了,速度也变快了,区区
的机子算出所有的362880个排列只需要3.9秒~~~
也就是说,虽然未必在时空上达到题目要求,但的确有可能有人几分钟就先拿到结果(不是区区,
版本一和版本二上的修改,花了区区不少时间)~
那现在我们假设时间要求是1秒吧~这个算法行得通么?应该没问题,如果对perl的运行效率和内存
字符串的操作有深入理解,应该知道如果用C语言来实现,可以优化到一秒内的
/**
********************************************
*/
#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#define TOP 9
char * num_str;
char * long_permute;
int long_idx;
int factor( int n_){
return n_ == 1 ? 1 :factor(n_ - 1 ) * n_;
}
void fill_n( char * arr_, int n){
int i;
for (i = 0 ; i < n; ++ i){
arr_[i] = ' 0 ' + i + 1 ;
}
arr_[i] = 0 ;
}
void make_permute( int deep_){
int i,k;
char t;
if (TOP - 1 == deep_){
strcpy(long_permute + long_idx, num_str);
long_idx += TOP + 1 ;
return ;
}
for (i = 0 ; i < (TOP - deep_); ++ i){
t = num_str[deep_ + i];
for ( k = deep_ + i; k > deep_ ; -- k){
num_str[k] = num_str[k - 1 ];
}
num_str[deep_] = t;
make_permute(deep_ + 1 );
for ( k = deep_; k < deep_ + i; ++ k){
num_str[k] = num_str[k + 1 ];
}
num_str[deep_ + i] = t;
}
}
void output( void ){
FILE * outfile;
int i,len;
len = factor(TOP);
outfile = fopen( " out1.txt " , " w " );
for (i = long_idx = 0 ; i < len; ++ i, long_idx += TOP + 1 ){
fprintf(outfile, " %s " ,long_permute + long_idx);
}
}
int main( void ){
num_str = malloc( (TOP + 1 ) * sizeof( char ) );
long_permute = malloc( factor(TOP) * sizeof( char ) * (TOP + 1 ) );
long_idx = 0 ;
fill_n(num_str,TOP);
make_permute( 0 );
output();
return 0 ;
}
/** ******************************************** */
#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#define TOP 9
char * num_str;
char * long_permute;
int long_idx;
int factor( int n_){
return n_ == 1 ? 1 :factor(n_ - 1 ) * n_;
}
void fill_n( char * arr_, int n){
int i;
for (i = 0 ; i < n; ++ i){
arr_[i] = ' 0 ' + i + 1 ;
}
arr_[i] = 0 ;
}
void make_permute( int deep_){
int i,k;
char t;
if (TOP - 1 == deep_){
strcpy(long_permute + long_idx, num_str);
long_idx += TOP + 1 ;
return ;
}
for (i = 0 ; i < (TOP - deep_); ++ i){
t = num_str[deep_ + i];
for ( k = deep_ + i; k > deep_ ; -- k){
num_str[k] = num_str[k - 1 ];
}
num_str[deep_] = t;
make_permute(deep_ + 1 );
for ( k = deep_; k < deep_ + i; ++ k){
num_str[k] = num_str[k + 1 ];
}
num_str[deep_ + i] = t;
}
}
void output( void ){
FILE * outfile;
int i,len;
len = factor(TOP);
outfile = fopen( " out1.txt " , " w " );
for (i = long_idx = 0 ; i < len; ++ i, long_idx += TOP + 1 ){
fprintf(outfile, " %s " ,long_permute + long_idx);
}
}
int main( void ){
num_str = malloc( (TOP + 1 ) * sizeof( char ) );
long_permute = malloc( factor(TOP) * sizeof( char ) * (TOP + 1 ) );
long_idx = 0 ;
fill_n(num_str,TOP);
make_permute( 0 );
output();
return 0 ;
}
/** ******************************************** */
在区区的机器上,这个C程序只用0.3秒就把所有的排列输出到out1.txt。。呵呵。。看来
最简单的方法在时间上都能满足要求了。。
上面的三个程序,并不是为了表明C比perl要快3.9/0.3-1倍,因为在最后一个C版程序中,
区区自行管理内存,省去了很多不必要的操作,这个在perl当中也是可以优化实现的。。
写了一大堆程序之后,回来原先贴子上的话题:怎么加快编程速度。。
那区区认为,通过上面的例子就知道,如果你手头有很好的工具语言,有类似问题的解决
经验,对自己所用的语言有很深入的理解,有很广的知识面,而且还经常编程,经常调试,
编程的速度自然而然就快了~~~
上面的算法,是区区连想都没想就已经存有的。全排列是以往做过的东西。
!!没有结束~
如果只是这样,那也太丢人了,C版的程序是很快,但是里面有个大得可怕的常数:
long_permute = malloc( factor(TOP) * sizeof(char) * (TOP+1) );
这条语句得占去好多MB的内存,如果对空间复杂度也有要求咋办?
不过还好,也没什么紧张的,因为上面那几个程序区区一开始在思考时都没有去写,区区
开头就在思考时空复杂度都很小的算法——————要不然,这道题应该没什么意思了~
其实准确地说,区区是在找规律:123456789、串、字典序、全排列,全都是有规律的说~
列了列草图,从小的做起吧
1 0
12 0
21 1
123 000
132 001
213 010
231 011
312 100
321 101
1234 0000
1243 0001
1324 0010
1342 0011
1423 0100
1432 0101
2134 0111
2143 1000
一开始很直觉地去找寻2进制数与排列的关系。。在长度1-3倒是隐约发现一个共同点:某位上
如果置1,排列上就与前一字符互换。很开心,只是一试就有个规律。开始求证么?NONONO,
四位排列也不多,所以也画出来。…………很快可以看出来,这个规律不对。
回头一想:当然不对,一个以阶乘增长,一个以乘幂增长。。。
等等~~~区区再回头一想:增长……阶乘增长————不正是规律么?
1 2 6 24
再看头一个字符出现的次数,呵呵……正好。。。
一个串的字典序排位,第一个字符与剩余字串排列数目(阶乘)有关,剩余字符串可以递推~
公式:对于长度为N的字符串,求第K的排列时,首字符应该是 ceil( K / ((N-1)!) )号字符
心算检验~~正确,写代码吧。。
#
perl
sub factor{
return $_ [ 0 ] == 1 ? 1 : factor( $_ [ 0 ] - 1 ) * $_ [ 0 ];
}
sub k_of_dict{
my ( $n , $k , $arr ) = @_ ;
for ( my $i = 0 ; $i < $n - 1 ; ++ $i ){
my $t = $k / factor( $n - $i - 1 );
$k = $k % factor( $n - $i - 1 );
next if int ( $t ) == 0 ;
splice @ $arr , $i , $t + 1 , $arr -> [ $i + $t ] , @{ $arr }[ $i .. $i + $t - 1 ];
}
}
# my $top=9;#输出到文件。
#my $FH;
#open $FH,">","out2.txt";
#my $len = factor($top);
#for (my $i=0;$i<$len;++$i){
# my @arr = 1..$top;
# k_of_dict($top,$i,@arr);
# print $FH @arr," ";
#}
###############################################
sub factor{
return $_ [ 0 ] == 1 ? 1 : factor( $_ [ 0 ] - 1 ) * $_ [ 0 ];
}
sub k_of_dict{
my ( $n , $k , $arr ) = @_ ;
for ( my $i = 0 ; $i < $n - 1 ; ++ $i ){
my $t = $k / factor( $n - $i - 1 );
$k = $k % factor( $n - $i - 1 );
next if int ( $t ) == 0 ;
splice @ $arr , $i , $t + 1 , $arr -> [ $i + $t ] , @{ $arr }[ $i .. $i + $t - 1 ];
}
}
# my $top=9;#输出到文件。
#my $FH;
#open $FH,">","out2.txt";
#my $len = factor($top);
#for (my $i=0;$i<$len;++$i){
# my @arr = 1..$top;
# k_of_dict($top,$i,@arr);
# print $FH @arr," ";
#}
###############################################
最后的这个程序,对于输出单个字典位置上的字符串,无论时间还是空间复杂度上,都不再
与N的阶乘有关。。相比与之前的版本,不再需要多余的内存了。。如果进一步发掘,可以找
出更好的算法。
丰富的数据结构知识、数学建模知识、分析统计可以找出更好的算法~~
本来如果还有时间,是打算写一个飞速的C版本。
但是完成这四个程序,脑力体操做得也很累~~
我们毕竟和那天常常在线做ACM题目的黄金圣斗士们不同,他们天天在为竞赛做准备的。。
台上一分钟,台下十年功,如果有人可以五分钟做出来,那说明他天份高,下苦功吧。
P.S:想这道题还没写Blog花时间,唉~