2010年2月27日 星期六

scanf & Enter

前幾天在練習字元的scanf 時,發現在作各兩次字元輸入時, 會造成無法成功讀取. 情況如下:

scanf("%c",&x);
scanf("%c",&y);

其中在第二次使用scanf時, 可能會有問題. 主要原因為第一次輸入字元x時,
我們需要須入Enter進入後面的statement, 但其在電腦會判讀我們所key的字元
同時也把Enter也記錄進去緩衝區裡頭, 等到第二次對字元y作scanf時,
它會直接判讀上一次留下的Enter鍵. 在此Enter其實是被當成一個字元\n被暫存起來,
這樣會造成無法正確輸入字元y.

如何有效避免這種問題, 在此來分享解決Enter暫存問題

方法(1)
scanf("%c",&x);
scanf("    %c",&y);  //在第二個scanf的%c前加入一個空格


方法(2)
scanf("%c/n",&x); //在第一個scanf的%c後加入一個/n
scanf("%c",&y);


方法(3)

scanf("%c%*c",&x);  //在第一個scanf的%c後加入%*c跳脫字元

scanf("%c",&y); 

方法(4)
x=getche();
y=getche();

方法(5)
scanf("%c",&x);
fflush(stdin);             //使用fflush函數來清空緩衝區資料
("%c",&y);

除了上述方法外, 仍有許多不同的方法可以克服, 如加上while判斷字元是否為/n, 如果不是才能執行,  有興趣的朋友可以去網路上作 scanf, Enter關鍵字搜尋. 就可以找到許多不同種的方法.

2010年2月15日 星期一

Tower of Hanoi

Tower of Hanoi(河內塔)
前幾日在練習C Language的遞迴時候,,裡頭的一小題,看了好久最後才懂得它的運作原理,簡單介紹一下它背景,



最早發明這個問題的人是法國 數學家愛德華·盧卡斯傳說中在印度某間寺院有三根柱子,上串64個金盤。依規定在每次只能搬移一片金盤至另一根柱子,且在過程中必須保持金盤由上至下是直徑由小至大的順序,意指任何一根柱子上,金盤都是直徑較小的被放在上層。預言說當這些盤子移動完畢,世界就會滅亡。也有人說這故事是盧卡斯自創,其真實性就不得而知了。若有興趣的人可以去網路上找尋相關資料,這裡就不再額外敘述了。

  這裡就用簡單的3個金盤作例子介紹,如上頭之動畫圖所示,假設有3支柱子分別為A, B, C,3金盤分別d1, d2, d3 (半徑 d1< d2 < d3)

(0)初始結構
A: d3, d2, d1
B:
C:

(1)將d1由A移至C
A: d3, d2
B:
C: d1

(2)將d2由A移至B
A: d3
B: d2
C: d1

(3)將d1由C移至B
A: d3
B: d2, d1
C:

(4)將d3由A移至C
A:
B: d2, d1
C: d3

(5)將d1由B移至A
A: d1
B: d2
C: d3
2, d1
(6)將d2由B移至C
A: d1
B:
C: d3, d2
1
(7)將d1由A移至C,結束
A:
B:
C: d3, d2, d1

這裡不細說內部的流程步驟,只用簡單規則來說明。因為大半徑的金盤須放置目的柱最底層,故以此小範例作解釋,如果要把d3從A柱(出發柱)放置C柱(目的柱),那必須把本來在A柱中d3以上的金盤(d2, d1)想辦法移置B柱(緩衝柱) [參考第(4)步驟],這樣才有辦法把d3由A柱直接移置C柱。等到移完d3,就必須再把原來B柱中的金盤全部移到C柱 [參考第(6)步驟]。

故可以歸納出一簡單的規則,若要移動第n個金盤,須依序考慮以下三大步驟,
(1) 先把第1~n-1個金盤從出發柱移到緩衝柱
(2) 把第n個金盤從出發柱移到目的柱
(3) 再把第1~n-1個金盤從緩衝柱移到目的柱

將上述三大步驟利用遞迴的方式,就可求解出在n個金盤下要如何在3支柱子上作移動。程式碼如下,
#include < stdio.h >
#include < stdlib.h >

void hanoi(int t, char A, char B, char C); //宣告一個河內塔公式,第一個變數為計數器,第二個變數為出發柱,第三個變數為緩衝柱,第四個變數為目的柱
int time = 0;

int main(void)
{
int n;
printf("請輸入金盤數:");
scanf("%d", &n);
hanoi(n, 'A', 'B', 'C');
printf("移動 %d 層金盤共需移動 %d 次\n", n, time);
return 0;
}

void hanoi(int n, char A, char B, char C)
{
if (n == 1)
{
printf("%d: 將第 %d 個金盤由 %c 移到 %c\n", time++, n, A, C); //當只有一個金盤就直接從出發柱移到目的柱
}
else
{
hanoi(n - 1, A, C, B); //把第1~n-1個金盤從出發柱移到緩衝柱
printf("%d: 將第 %d 個金盤由 %c 移到 %c\n", time++, n, A, C); 把第n個金盤從出發柱移到目的柱
hanoi(n - 1, B, A, C); //把第1~n-1個金盤從緩衝柱移到目的柱
}
}


以上之是考慮用遞迴方式去作計算,建議可以使用debug模式去追蹤hanoiy函數,這樣你就可以了解整個運算過程。

2010年2月13日 星期六

The Beauty of Rhombus

Rhombus(菱形)
利用C語言中的基本FOR迴圈列印出一菱形, 如下所示: (假定邊長為3, 實心菱形與空心菱形)



若遇到類似這種幾合問題的列印, 通常可以利用分解方式來作處理(將菱形拆解為上三角形與下三角形輸出). 在此小弟分享了一個無須分解菱形的一次列印方式, 以簡單的FOR迴圈即可處理.

首先以簡單的邊長為3作例子, 該菱形的除了星號的呈現外, 最重要的空白的使用. 在邊長為3的菱形中, 共跳5次行, 依各行來看皆有一對應的空格數與星號格數, 如下所示: (BS=Blank Space, S=Star)

第1行: 2 BS, 1 S
第2行: 1 BS, 3 S
第3行: 0 BS, 5 S
第4行: 1 BS, 3 S
第5行: 2 BS, 1 S

假設 i 控制行數, j 控制空白數, k 控制星號數.
從上述數字可看出彼此的規律, 以行數對空百數來看, 可發現絕對值 i-3 會等於 j.

第1行: |1-3| BS, 1 S
第2行: |2-3| BS, 3 S
第3行: |3-3| BS, 5 S
第4行: |4-3| BS, 3 S
第5行: |5-3| BS, 1 S

在第3行中星號為最多, 共5個, 依此為上限值, 以行數對星號數來看, 可發現絕對值 i-3之兩倍與5相差位數為星號個數, 以第一行為例, 2*|1-3|-5=1, 其差值為1剛好就代表星號個數, 其餘各行依此類推.

第1行: |1-3| BS, 5-2*|1-3| S
第2行: |2-3| BS, 5-2*|2-3| S
第3行: |3-3| BS, 5-2*|3-3| S
第4行: |4-3| BS, 5-2*|4-3| S
第5行: |5-3| BS, 5-2*|5-3| S

從上述就可以看出一個基本通式, 紅色的3剛好為一開始設定的邊長數, 藍色的5則為2倍的邊長數-1. 進一步假設 i 控制行數, j 控制空白數, k 控制星號數, e為預設菱形邊長數, 其中 i 的初始值為0, 其中還可以額外發現行數的上限值為則為2倍的邊長數-1, 故通式為,

第 i+1 行: |i+1-e| BS, 2*e-1-2*|i+1-e| S
第 i+2 行: |i+2-e| BS, 2*e-1-2*|i+2-e| S
第 i+3 行: |i+3-e| BS, 2*e-1-2*|i+3-e| S
第 i+4 行: |i+4-e| BS, 2*e-1-2*|i+4-e| S
第 i+2*e-1 行: |i+5-e| BS, 2*e-1-2*|i+5-e| S

其程式碼如下:
#include < stdio.h >stdio.h>
#include < stdlib.h >
#include < math.h >
int main()
{
int i,j,k,e; //i 控制行數, j 控制空白數, k 控制星號數, e為預設菱形邊長數
printf("input a number:"); //輸入菱形邊長數
scanf("%d",&e);
for(i=1;i<=2*e-1;i++) //行數從1開始, 上限為2倍的邊長數-1
{
for(j=abs(i-e);j>0;j--) //控制各行的空白數, 須列印abs(i-e)個
{
printf(" ");
}
for(k=abs(i-e)*2;k<2*e-1;k++) //控制各行的星號數, 須列印2*e-1-2*abs(i-e)個
{
printf("*");
}
printf("\n");
}
system("PAUSE");
return 0;
}

如果要列印空心菱形, 其實只要在星號列印時,作個小小的微調即可, 星號數下限為2*abs(i-e), 上限為2*z-1, 故在星號列印上加入上下值限制式. 如下所示.

for(k=abs(i-z)*2;k<2*z-1;k++)
{
if(k==abs(i-z)*2||k==2*z-2) // 加入星號數上, 下限值
printf("*");
else
printf(" ");
}


綜合上述程式, 即可在不用拆解菱形情況下, 以4變數 (邊長, 行數, 空白數, 星號數) 一次到位直接列印輸出任意邊長的實心菱形與空心菱形.