算法習題---3.08循環小數(UVa202)
阿新 • • 發佈:2019-05-06
是否 兩個 是不是 一位 輸出 mem ++ void pri
一:題目
輸入整數a和b(0<=a<=3000,1<=b<=3000),輸出a/b的循環小數表示以及循環節長度。 例如,a=5,b=43,小數表示為0.(116279069767441860465),循環字節長度為21
當循環節長度超過50時,後面使用...代替,不進行輸出後面部分
(一)樣例輸入
76 25 5 43 1 397
(二)樣例輸出
76/25 = 3.04(0) 1 = number of digits in repeating cycle 5/43 = 0.(116279069767441860465) 21 = number of digits inrepeating cycle 1/397 = 0.(00251889168765743073047858942065491183879093198992...) 99 = number of digits in repeating cycle
二:題目分析
(一)余數用作判斷循環節的依據:(循環節長度大小不會超過余數大小,原因如下:)
1/6-->10/6=1...4 4/6-->40/6=6...4出現兩次4,所以一定會出現循環節 4/6-->40/6=6...4出現三次4,所以循環節就在二三次之間(即商6)
所以用一個數組記錄余數,大小不會超過除數大小
(二)同時也要記錄商,用於記錄循環節
若是除數為P,則余數為0->P-1;所以在P次之間一定會出現兩個相同余數 //(一中原因)
-->一定會出現循環節
所以我們再記錄一次余數,當出現某個余數出現為3次,那麽循環節在兩個余數位置中間
由於商數組和余數數組對應,所以可以輕松找到循環節
(三)商數組和余數數組大小
余數數組大小應該為除數大小 //余數只需要除數大小,用於記錄余數出現次數
商數組大小應該為除數大小的兩倍 //商用於記錄對應余數中的商值,由於每一個余數必須出現3次,所以我們記錄的商必須足夠長,足以支持余數次數到達3
例:
其中0<=a<=3000,1<=b<=3000---> 所以余數數組不會超過3000 商數組大小不超過6000
註:上面的商和余數數組只記錄小數點後的值,不記錄整數值
三:代碼分析(使用76/25 ,1/6 ,5/43分析)
(一)處理整數部分:只剩下會出現小數部分的被除數進行下一步處理
//開始處理數據 //1.獲取整數 //先記錄整數:可以在記錄時直接輸出,節省篇幅 printf("%d/%d = ", a, b); if (!(a / b)) printf("0"); else while (a / b) { printf("%d", a / b); a %= b; } printf(".");
(1)76/25
76/25=3...1
代碼輸出76/25 = 3.
(2)1/6
直接為小數,所以按照代碼輸出1/6 = 0.
(3)5/43
直接為小數,所以按照代碼輸出5/43 = 0.
(二)獲取商數組和余數數組
//2.開始記錄商和余數 i = 0; while (1) //例如:1/6 { //記錄商和余數 res[i] = a * 10 / b; //10/6=1 //1.記錄商 rem[a * 10 % b]++; //10%6=4 //2.記錄對應余數位置出現次數 //註意兩次和三次都是只記錄一次即可 if (rem[a * 10 % b] == 2&&!two_flag) //3.若是出現兩次,則開始進行記錄 只記錄第一個出現兩次的余數 { r_s = i; two_flag = 1; } if (rem[a * 10 % b] == 3) //4.若是出現3次,則確定了循環節位置,直接跳出 { r_e = i; break; } //處理被除數 a = a * 10 % b; //a = 4 i++; }
(1)76/25
10/25=0...10 0 1 第一個為商下標,第二個為余數次數 100/25=4...0 1 1 0/25=0...0 2 2 余數出現兩次時的商0和出現一次時的商4不一致 0/25=0...0 3 3
res = { 0 4 0 0} rem = { 3 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
(2)1/6
10/6=1...4 0 1 第一個為商下標,第二個為余數次數 40/6=6...4 1 2 余數出現兩次時的商6和出現一次時的商1不一致 40/6=6...4 2 3
res = { 1 6 6 } rem = { 0 0 0 0 3 0 }
(3)5/43
50/43=1...7 0 1 70/43=1...27 ... .....=1...7 21 2 余數出現兩次時的商和出現一次時的商一致 ... .....=1...7 42 3
(三):進行數據輸出
註意記錄數據時要區分0.1(6) 3.04(0) 和0.(11....) 不同之處在於循環節是不是從小數第一位開始 處理原則在於看1,2兩次余數相同時,商是否一致(上面案例)
int rep_count = r_s; if (res[r_s] != res[2 * r_s - r_e]) //若是余數第一次和第二次對應的商值不一樣,則加一 rep_count++; for (j = 0; j <rep_count ;j++) //註意,先看下面註釋,可以知道,真正循環節結束在我們獲取的第二個相同余數位置-->即r_s { if (j == 2 * r_s - r_e && rep_count==r_s) //註意這裏:不是j-r_s printf("("); else if (j == 2 * r_s - r_e+1&&rep_count==r_s+1) //遇到循環節不是從小數點後開始,則向後一位 printf("("); printf("%d", res[j]); if (j +r_e- 2*r_s == 49) //不是j-r_s -->因為我們雖然確定r_s到r_e時一個循環節,但是r_s是第二個相同余數開始,r_e是第三個余數開始 { //4 4 4--->r_s是第二個開始,r_e是第三個開始,但是真的循環節是從第一個4到第二個4 printf("..."); break; } } printf(")\n"); printf(" %d = number of digits in repeating cycle\n", r_e - r_s);
四:代碼實現
void test33() { int a, b; int zs[4]; //存放整數,例如結果10.13161616,這裏用於存放10 //由於程序變更,後面沒有用到zs,但是為了便於理解,留在這 int res[MAX_REP]; //存放商 int rem[MAX_REP]; //存放余數 int two_flag; //註意兩次和三次都是只記錄一次即可,但是由於三次直接break,所以不做標誌 int i, j; int r_s, r_e; //循環節開始,循環節結束 FILE* fi = freopen("data2.in", "r", stdin); freopen("data2.out", "w", stdout); while (!feof(fi)) { scanf("%d", &a); scanf("%d", &b); memset(zs, 0, sizeof(zs)); memset(res, 0, sizeof(res)); memset(rem, 0, sizeof(rem)); two_flag = 0; //開始處理數據 //1.獲取整數 //先記錄整數:可以在記錄時直接輸出,節省篇幅 printf("%d/%d = ", a, b); if (!(a / b)) printf("0"); else while (a / b) { printf("%d", a / b); a %= b; } printf("."); //2.開始記錄商和余數 i = 0; while (1) //例如:1/6 { //記錄商和余數 res[i] = a * 10 / b; //10/6=1 rem[a * 10 % b]++; //10%6=4 //註意兩次和三次都是只記錄一次即可 if (rem[a * 10 % b] == 2&&!two_flag) //若是出現兩次,則開始進行記錄 { r_s = i; two_flag = 1; } if (rem[a * 10 % b] == 3) { r_e = i; break; } //處理被除數 a = a * 10 % b; //a = 4 i++; } //開始記錄數據 //註意記錄數據時要區分0.1(6) 3.04(0) 和0.(11....) //不同之處在於循環節是不是從小數第一位開始 //處理原則在於看1,2兩次余數相同時,商是否一致 int rep_count = r_s; if (res[r_s] != res[2 * r_s - r_e]) rep_count++; for (j = 0; j <rep_count ;j++) //註意,先看下面註釋,可以知道,真正循環節結束在我們獲取的第二個相同余數位置-->即r_s { if (j == 2 * r_s - r_e && rep_count==r_s) //註意這裏:不是j-r_s printf("("); else if (j == 2 * r_s - r_e+1&&rep_count==r_s+1) //遇到循環節不是從小數點後開始,則向後一位 printf("("); printf("%d", res[j]); if (j +r_e- 2*r_s == 49) //不是j-r_s -->因為我們雖然確定r_s到r_e時一個循環節,但是r_s是第二個相同余數開始,r_e是第三個余數開始 { //4 4 4--->r_s是第二個開始,r_e是第三個開始,但是真的循環節是從第一個4到第二個4 printf("..."); break; } } printf(")\n"); printf(" %d = number of digits in repeating cycle\n", r_e - r_s); } freopen("CON", "r", stdin); freopen("CON", "w", stdout); }
算法習題---3.08循環小數(UVa202)