Ý tưởng
Sau một thời gian đọc và suy ngẫm các bài viết của anh [MENTION=125068]pctcom[/MENTION] và anh [MENTION=204111]thangbk92[/MENTION]. Dâng nhận thấy với giải pháp dùng bảng tra này, vi điều khiển thực hiện rất nhanh, và đơn giản hơn nhiều so với tính trực tiếp như của cụ Hồ Ngọc Đức, tuy nhiên giải thuật của các anh đều chưa thật chính xác. Thế nên hôm nay Dâng sẽ trình bày một giải thuật mới phát triển từ ý tưởng của hai anh trên. Mục tiêu của giải thuật mới này là:
1. Nâng cao độ chính xác của giải thuật
2. Giảm kích thước của bảng tra.
Mục tiêu thứ nhất thì Dâng không đảm bào được (chỉ là tương đối thôi và Dâng cảm thấy như vậy), mục tiêu thứ 2 Dâng đảm bảo sẽ đạt được. Để lưu thông tin của mỗi tháng Dâng chỉ dùng 2 bytes thôi. Như vậy nếu bảng tra 100 năm (từ 1/1/2000 đến 31/12/2099) thì chỉ cần khoảng 2.4 kbytes. Cụ thể, trong mỗi tháng, các thông tin cần lưu là:
1. Ngày âm lịch đầu tháng dương lịch (N_AL_DT_DL). Do mỗi tháng âm lịch có khoảng từ 29÷30 ngày, nên biến này lưu trong 5 bits.
2. Tháng âm lịch đầu tháng dương lịch (T_AL_DT_DL). Do tháng có giá trị từ 1÷12, nên biến này lưu trong 3 bits.
3. Số ngày cảu tháng âm lịch đầu tháng dương lịch (SN_CT_AL). Do tháng âm lịch 29 hoặc 30 ngày, nên biến này lưu trong 1 bit (0 là 29 ngày, 1 là 30 ngày).
4. Còn một biến thứ 4, biến này chỉ cần dùng 1 bit, còn chức năng của nó Dâng sẽ nói sau.
Như vậy tổng cộng Dâng dùng 5 + 4 + 1 + 1 = 11 bits cho mỗi tháng. Còn dư 5 bits (so với mục tiêu 2 byte ban đầu đặt ra), do đó Dâng sẽ lưu tiếp biến thứ 5.
5. Số ngày của tháng dương lịch (SN_CT_DL). Do số ngày của tháng dương lịch chỉ có thể là 28, 29, 30 hoặc 31 nên chỉ cần 2 bít (00 cho 28, 01 cho 29, 10 cho 30 và 11 cho 31). Trong giải thuật này không hề sử dụng biến thứ 5 này, nhưng vẫn để sẵn trong bảng tra cho những lúc cần dùng (cho chức năng hiệu chỉnh ngày tháng chẳng hạn).
Như vậy vẫn còn dư 2 bits không biết để làm gì nữa, thì đành bỏ trống vậy.
Giải thuật
Dâng chia tháng thành hai loại tháng như sau:
1. Tháng bình thường: Là tháng có nhiều nhất 1 lần chuyển đổi tháng âm lịch. Đa số các tháng thuộc loại này. Ví dụ tháng 6/2000, có một lần chuyển đổi tháng âm duy nhất (từ tháng 4 sang tháng 5). Xem hình ở dưới:
Với tháng bình thường này, cách xác định ngày tháng âm lịch rất dễ.
Ngày âm = Ngày dương + Ngày âm lịch đầu tháng dương lịch - 1;
Nếu ngày âm <= Số ngày của tháng âm lịch đầu tháng dương lịch thì giữ nguyên ngày âm và tháng âm = tháng âm lịch đầu tháng dương lịch
Ngược lại ngày âm = ngày âm - số ngày của tháng âm lịch đầu tháng dương lịch và tháng âm = tháng âm lịch đầu tháng dương lịch của đầu tháng tháng dương lịch tiếp theo (tra được trong thông tin của tháng kế). Không lấy tháng âm lịch của tháng dương lịch + 1 nhé, nó sẽ sai trong trường hợp tháng âm lịch bị nhuận hoặc tháng âm lịch là tháng 1.
2. Tháng dị thường: Là tháng có 2 lần chuyển đổi tháng âm lịch. Ví dụ tháng 7/2000, có hai lần chuyển đổi tháng âm lịch, lần thứ nhất là từ tháng 5 sang tháng 6 và lần thứ hai là từ tháng 6 sang tháng 7. Xem hình:
Có rất nhiều tháng dị thường từ năm 1949 trở về sau: 5/2049, 7/2057, 8/2065, 5/2068, 3/2071, 8/2084, 7/2095
Tháng dị thường sẽ có cách tính riêng. Vì vậy, ta phải tìm cách nhận ra đâu là tháng dị thường.
Nếu (Ngày âm lịch đầu tháng dương lịch == số ngày của tháng âm lịch đầu tháng dương lịch) && (Ngày âm lịch đầu tháng dương lịch của tháng tiếp theo == 2) thì đó là tháng dị thường.
Cái này Dâng ngồi bóc lịch mấy năm mới kết luận được.
Xác định được tháng dị thường rồi thì tính ngày tháng âm lịch cho nó là không khó.
Nếu ngày == 1 thì ngày âm lịch = Ngày âm lịch đầu tháng dương lịch và tháng âm lịch bằng = tháng âm lịch đầu tháng dương lịch.
Nếu ngày == 31 thì ngày âm lịch = 1 và tháng âm lịch = Tháng âm lịch đầu tháng dương lịch của tháng tiếp theo.
Các ngày còn lại, ngày âm lịch = ngày - 1. Còn tháng âm lịch làm sao tính???
Nếu tháng âm lịch đầu tháng dương lịch của tháng tiếp theo - tháng âm lịch đầu tháng dương lịch của tháng hiện tại == 2 thì thâng âm lịch = tháng âm lịch đầu tháng dương lịch hiện tại - 1. Nhưng nếu tháng âm lịch đầu tháng dương lịch của tháng tiếp theo - tháng âm lịch đầu tháng dương lịch của tháng hiện tại == 1 thì tháng âm lịch hiện tại là tháng âm lịch nhuận và nó có thể bằng tháng âm lịch của đầu tháng dương lịch hiện tại hoặc bằng tháng âm lịch của đầu tháng dương lịch tiếp theo, không có cách gì xác định được cả. Lúc này ta sử dụng tới biến thứ 4 của bảng tra, biến này quy ước, nếu nó bằng 1 thì tháng âm lịch = tháng âm lịch của đầu tháng dương lịch hiện tại, nếu nó bằng 0 thì tháng âm lịch = tháng âm lịch của đầu tháng dương lịch của tháng tiếp theo. Và biến này mình đặt tên là Tháng Nhuận Bằng Tháng Hiện Tại (TN_B_THT).
Sau khi xác định được ngày tháng thì năm âm lịch rất dễ tính:
Nếu tháng âm lịch <= tháng dương lich thì năm âm lich = năm dương lịch
Ngược lại thì năm âm lịch = năm dương lịch - 1
Ví dụ minh họa giải thuật
Ví dụ với tháng 6/2000 (xem hình phía trên) thì các thông tin cần lưu sẽ là:
N_AL_DT_DL = 29
T_AL_DT_DL = 4
SN_CT_AL = 29 (lưu là 0 trong bảng)
TN_B_THT = 0 (đây không phải là tháng dị thường nên giá trị này là 0 hay là 1 gì cũng được)
SN_CT_DL = 30 (lưu là 2 trong bảng tra)
Với tháng 7/2000 (xem hình phía trên) thì các thông tin cần lưu sẽ là:
N_AL_DT_DL = 30
T_AL_DT_DL = 5
SN_CT_AL = 30 (lưu là 1 trong bảng)
TN_B_THT = 0 (đây là tháng dị thường nhưng không phải là tháng nhuận nên giá trị này là 0 hay là 1 gì cũng được)
SN_CT_DL = 31 (lưu là 3 trong bảng tra)
Với tháng 8/2000
N_AL_DT_DL = 2
T_AL_DT_DL = 7
SN_CT_AL = 29 (lưu là 0 trong bảng)
TN_B_THT = 0
SN_CT_DL = 31 (lưu là 3 trong bảng tra)
Đang lưu bài viết tự dưng "Cúp điện" có điện lại thì mở lên chỉ còn có nửa bài. Các MOD có cách nào cho Dâng lấy lại bài trước khi Lưu nửa chừng do cúp điện không?? Giờ mà viết lại viết không nỗi nữa.....
Sau một thời gian đọc và suy ngẫm các bài viết của anh [MENTION=125068]pctcom[/MENTION] và anh [MENTION=204111]thangbk92[/MENTION]. Dâng nhận thấy với giải pháp dùng bảng tra này, vi điều khiển thực hiện rất nhanh, và đơn giản hơn nhiều so với tính trực tiếp như của cụ Hồ Ngọc Đức, tuy nhiên giải thuật của các anh đều chưa thật chính xác. Thế nên hôm nay Dâng sẽ trình bày một giải thuật mới phát triển từ ý tưởng của hai anh trên. Mục tiêu của giải thuật mới này là:
1. Nâng cao độ chính xác của giải thuật
2. Giảm kích thước của bảng tra.
Mục tiêu thứ nhất thì Dâng không đảm bào được (chỉ là tương đối thôi và Dâng cảm thấy như vậy), mục tiêu thứ 2 Dâng đảm bảo sẽ đạt được. Để lưu thông tin của mỗi tháng Dâng chỉ dùng 2 bytes thôi. Như vậy nếu bảng tra 100 năm (từ 1/1/2000 đến 31/12/2099) thì chỉ cần khoảng 2.4 kbytes. Cụ thể, trong mỗi tháng, các thông tin cần lưu là:
1. Ngày âm lịch đầu tháng dương lịch (N_AL_DT_DL). Do mỗi tháng âm lịch có khoảng từ 29÷30 ngày, nên biến này lưu trong 5 bits.
2. Tháng âm lịch đầu tháng dương lịch (T_AL_DT_DL). Do tháng có giá trị từ 1÷12, nên biến này lưu trong 3 bits.
3. Số ngày cảu tháng âm lịch đầu tháng dương lịch (SN_CT_AL). Do tháng âm lịch 29 hoặc 30 ngày, nên biến này lưu trong 1 bit (0 là 29 ngày, 1 là 30 ngày).
4. Còn một biến thứ 4, biến này chỉ cần dùng 1 bit, còn chức năng của nó Dâng sẽ nói sau.
Như vậy tổng cộng Dâng dùng 5 + 4 + 1 + 1 = 11 bits cho mỗi tháng. Còn dư 5 bits (so với mục tiêu 2 byte ban đầu đặt ra), do đó Dâng sẽ lưu tiếp biến thứ 5.
5. Số ngày của tháng dương lịch (SN_CT_DL). Do số ngày của tháng dương lịch chỉ có thể là 28, 29, 30 hoặc 31 nên chỉ cần 2 bít (00 cho 28, 01 cho 29, 10 cho 30 và 11 cho 31). Trong giải thuật này không hề sử dụng biến thứ 5 này, nhưng vẫn để sẵn trong bảng tra cho những lúc cần dùng (cho chức năng hiệu chỉnh ngày tháng chẳng hạn).
Như vậy vẫn còn dư 2 bits không biết để làm gì nữa, thì đành bỏ trống vậy.
Giải thuật
Dâng chia tháng thành hai loại tháng như sau:
1. Tháng bình thường: Là tháng có nhiều nhất 1 lần chuyển đổi tháng âm lịch. Đa số các tháng thuộc loại này. Ví dụ tháng 6/2000, có một lần chuyển đổi tháng âm duy nhất (từ tháng 4 sang tháng 5). Xem hình ở dưới:
Với tháng bình thường này, cách xác định ngày tháng âm lịch rất dễ.
Ngày âm = Ngày dương + Ngày âm lịch đầu tháng dương lịch - 1;
Nếu ngày âm <= Số ngày của tháng âm lịch đầu tháng dương lịch thì giữ nguyên ngày âm và tháng âm = tháng âm lịch đầu tháng dương lịch
Ngược lại ngày âm = ngày âm - số ngày của tháng âm lịch đầu tháng dương lịch và tháng âm = tháng âm lịch đầu tháng dương lịch của đầu tháng tháng dương lịch tiếp theo (tra được trong thông tin của tháng kế). Không lấy tháng âm lịch của tháng dương lịch + 1 nhé, nó sẽ sai trong trường hợp tháng âm lịch bị nhuận hoặc tháng âm lịch là tháng 1.
2. Tháng dị thường: Là tháng có 2 lần chuyển đổi tháng âm lịch. Ví dụ tháng 7/2000, có hai lần chuyển đổi tháng âm lịch, lần thứ nhất là từ tháng 5 sang tháng 6 và lần thứ hai là từ tháng 6 sang tháng 7. Xem hình:
Có rất nhiều tháng dị thường từ năm 1949 trở về sau: 5/2049, 7/2057, 8/2065, 5/2068, 3/2071, 8/2084, 7/2095
Tháng dị thường sẽ có cách tính riêng. Vì vậy, ta phải tìm cách nhận ra đâu là tháng dị thường.
Nếu (Ngày âm lịch đầu tháng dương lịch == số ngày của tháng âm lịch đầu tháng dương lịch) && (Ngày âm lịch đầu tháng dương lịch của tháng tiếp theo == 2) thì đó là tháng dị thường.
Cái này Dâng ngồi bóc lịch mấy năm mới kết luận được.
Xác định được tháng dị thường rồi thì tính ngày tháng âm lịch cho nó là không khó.
Nếu ngày == 1 thì ngày âm lịch = Ngày âm lịch đầu tháng dương lịch và tháng âm lịch bằng = tháng âm lịch đầu tháng dương lịch.
Nếu ngày == 31 thì ngày âm lịch = 1 và tháng âm lịch = Tháng âm lịch đầu tháng dương lịch của tháng tiếp theo.
Các ngày còn lại, ngày âm lịch = ngày - 1. Còn tháng âm lịch làm sao tính???
Nếu tháng âm lịch đầu tháng dương lịch của tháng tiếp theo - tháng âm lịch đầu tháng dương lịch của tháng hiện tại == 2 thì thâng âm lịch = tháng âm lịch đầu tháng dương lịch hiện tại - 1. Nhưng nếu tháng âm lịch đầu tháng dương lịch của tháng tiếp theo - tháng âm lịch đầu tháng dương lịch của tháng hiện tại == 1 thì tháng âm lịch hiện tại là tháng âm lịch nhuận và nó có thể bằng tháng âm lịch của đầu tháng dương lịch hiện tại hoặc bằng tháng âm lịch của đầu tháng dương lịch tiếp theo, không có cách gì xác định được cả. Lúc này ta sử dụng tới biến thứ 4 của bảng tra, biến này quy ước, nếu nó bằng 1 thì tháng âm lịch = tháng âm lịch của đầu tháng dương lịch hiện tại, nếu nó bằng 0 thì tháng âm lịch = tháng âm lịch của đầu tháng dương lịch của tháng tiếp theo. Và biến này mình đặt tên là Tháng Nhuận Bằng Tháng Hiện Tại (TN_B_THT).
Sau khi xác định được ngày tháng thì năm âm lịch rất dễ tính:
Nếu tháng âm lịch <= tháng dương lich thì năm âm lich = năm dương lịch
Ngược lại thì năm âm lịch = năm dương lịch - 1
Ví dụ minh họa giải thuật
Ví dụ với tháng 6/2000 (xem hình phía trên) thì các thông tin cần lưu sẽ là:
N_AL_DT_DL = 29
T_AL_DT_DL = 4
SN_CT_AL = 29 (lưu là 0 trong bảng)
TN_B_THT = 0 (đây không phải là tháng dị thường nên giá trị này là 0 hay là 1 gì cũng được)
SN_CT_DL = 30 (lưu là 2 trong bảng tra)
Với tháng 7/2000 (xem hình phía trên) thì các thông tin cần lưu sẽ là:
N_AL_DT_DL = 30
T_AL_DT_DL = 5
SN_CT_AL = 30 (lưu là 1 trong bảng)
TN_B_THT = 0 (đây là tháng dị thường nhưng không phải là tháng nhuận nên giá trị này là 0 hay là 1 gì cũng được)
SN_CT_DL = 31 (lưu là 3 trong bảng tra)
Với tháng 8/2000
N_AL_DT_DL = 2
T_AL_DT_DL = 7
SN_CT_AL = 29 (lưu là 0 trong bảng)
TN_B_THT = 0
SN_CT_DL = 31 (lưu là 3 trong bảng tra)
Đang lưu bài viết tự dưng "Cúp điện" có điện lại thì mở lên chỉ còn có nửa bài. Các MOD có cách nào cho Dâng lấy lại bài trước khi Lưu nửa chừng do cúp điện không?? Giờ mà viết lại viết không nỗi nữa.....
Comment