本文的內容本身來自一個名校計算機生的一次面試經歷,呵呵,沒錯,你猜對了,肯定 不是我
個人很喜歡這兩道題,可能題目原本不止兩道,當然,我這里這分析我很喜歡的兩道。
1.寫一個函數計算當參數為n(n很大)時的值 1-2+3-4+5-6+7......+n
當我看了面試經過后,我覺得很有代表性,於是,我就拿着這個題目去問我的一些同學,我想看看大家拿到這個題目的第一實現方式。大家給我的反應也在意料之中,說是直接寫個for循環。自此,大家都犯了一個程序員最愛犯的錯誤,那就是把所有的工作都交給cpu去做。或許大家會說,現在的計算機,運行速度那么快,根本不用考慮執行這種程序給cpu帶來的效率問題。對於這個問題,我們下面在討論。
看這位名校生第一次給出的答案:
- long fn(long n)
- {
- long temp=0;
- int i,flag=1;
- if(n<=0)
- {
- printf("error: n must > 0);
- exit(1);
- }
- for(i=1;i<=n;i++)
- {
- temp=temp+flag*i;
- flag=(-1)*flag;
- }
- return temp;
- }
此答案面試官並不滿意,后來他又給出了第二個答案:
- long fn(long n)
- {
- long temp=0;
- int j=1,i=1,flag=1;
- if(n<=0)
- {
- printf("error: n must > 0);
- exit(1);
- }
- while(j<=n)
- {
- temp=temp+i;
- i=-i;
- i>0?i++:i--;
- j++;
- }
- return temp;
- }
我個人覺得對於一個剛畢業的大四面試者來說,已經很不容易,當然肯定有人能做的比這些更好,他把原代碼中用乘除的地方換成了加減法,我們知道計算機做加減比做乘除效率更高,因為很多計算機乘除會最終轉換成加減法來做。
面試官仍不滿意,但此時這名大四面試着着急之下想不到最佳方案了。只能求最優答案。
- long fn(long n)
- {
- if(n<=0)
- {
- printf("error: n must > 0);
- exit(1);
- }
- if(0==n%2)
- return (n/2)*(-1);
- else
- return (n/2)*(-1)+n;
- }
呵呵,我也很驚奇,的確,在n很大的時候,此算法不知要比上面的兩種算法強多少倍。
這就是為什么上面提到的,程序員不能把所有的工作都丟給計算機,計算機的cpu是給用戶用的,而不是給程序員用的。
"不要認為CPU運算速度快就 把所有的問題都推給它去做,程序員應該將代碼優化再優化,我們自己能做的決不要讓CPU做 ,因為CPU是為用戶服務的,不是為我們程序員服務的!”多么精辟的語言,我已經不想再說 什么了!
上面的算法我覺得已經很優了,但是我的一個同學給出了在上面算法的基礎上我認為更優的方法。
- long fn(long n)
- {
- if(n<=0)
- {
- printf("error: n must > 0);
- exit(1);
- }
- if(0==n%2)
- return (n>>1)*(-1);
- else
- return (n>>1)*(-1)+n;
- }
我們知道加減法效率比乘除的效率高,而移位運算在大多數情況下也是比加減的效率更高。
2.用一種技巧性的編程方法來用一個函數實現兩個函數的功能n為如:
fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6!
fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9!
現在用一個函數fn(int n,int flag)實現,當flag為0時 ,實現fn1功能,如果flag為1時實現fn2功能!要求還是效率,效率,效率!
給出的答案是:
定義一個二維數組 float t[2][5]存入[2!,3!,4!,5!,6!},{5! ,6! ,7!,8!,9!]然后給出一個循環:
for(i=0;i<6;i++)
{
temp=temp+n/t[flag][i];
}
呵呵,是不是很巧妙?典型的空間換時間的算法!