PDA

View Full Version : Làm tròn lên, làm tròn xuống theo block number



hai2hai
02-01-09, 07:41 PM
Mình có 1 bài nho nhỏ gửi tới mọi người:

Bài toán liên quan tới việc chấm công tự động bằng thẻ về việc đi muộn, về sớm đối với nhân viên trong công ty

Mục tiêu:
- Lựa chọn làm tròn lên theo Block: Giả sử nhân viên đi muộn là từ 1 đến 10 phút thì sẽ làm tròn lên thành 10 phút. Nếu đi muộn 11 đến 20 phút thì làm tròn là 20, v.v... Khi đó gọi số 10 là Block Number. (Cách này ko có lợi cho nhân viên)
- Lựa chọn làm tròn xuống theo Block: Giả sử nhân viên đi muộn là từ 1 đến 9 phút thì sẽ làm tròn xuống thành 0 phút. Nếu đi muộn 10 đến 19 phút thì làm tròn là 10 phút, v.v... Khi đó gọi số 10 là Block Number. Và trường hợp này gọi là làm tròn xuống (Cách này có lợi cho nhân viên)

Câu hỏi:
1. Hãy viết code để đáp ứng được việc trên
Ví dụ:
Private Function RoundUpByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
Private Function RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long

2. Tối ưu hóa những gì bạn viết, tốt nhất là chỉ cần 1 dòng thôi


Cheers,

ndu96081631
02-01-09, 08:07 PM
Ở đây mình vẫn chưa hiểu 1 chổ:
Theo như những gì mô tả ở trên thì bài toán này là tìm số block dựa vào thời gian đi trể ---> Vậy chỉ có 1 biến thôi chứ

ptm0412
02-01-09, 08:16 PM
Đúng là chỉ cần 1 dòng:

Private Function RoundUpByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
RoundUpByBlock = Int(lngNumber&, lngBlockNumber&) * lngBlockNumber& + lngBlockNumber&
End Function

Private Function RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
RoundDownByBlock = Int(lngNumber&, lngBlockNumber&) * lngBlockNumber&
End Function

hoangvuluan
02-01-09, 08:44 PM
Mình có 1 bài nho nhỏ gửi tới mọi người:

Bài toán liên quan tới việc chấm công tự động bằng thẻ về việc đi muộn, về sớm đối với nhân viên trong công ty

Mục tiêu:
- Lựa chọn làm tròn lên theo Block: Giả sử nhân viên đi muộn là từ 1 đến 10 phút thì sẽ làm tròn lên thành 10 phút. Nếu đi muộn 11 đến 20 phút thì làm tròn là 20, v.v... Khi đó gọi số 10 là Block Number. (Cách này ko có lợi cho nhân viên)
- Lựa chọn làm tròn xuống theo Block: Giả sử nhân viên đi muộn là từ 1 đến 9 phút thì sẽ làm tròn xuống thành 0 phút. Nếu đi muộn 10 đến 19 phút thì làm tròn là 10 phút, v.v... Khi đó gọi số 10 là Block Number. Và trường hợp này gọi là làm tròn xuống (Cách này có lợi cho nhân viên)

Câu hỏi:
1. Hãy viết code để đáp ứng được việc trên
Ví dụ:
Private Function RoundUpByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
Private Function RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long

2. Tối ưu hóa những gì bạn viết, tốt nhất là chỉ cần 1 dòng thôi


Cheers,


Tôi nghĩ chỉ cần dùng 2 hàm:
Ceiling(lngNumber&, lngBlockNumber) và Floor(lngNumber&, lngBlockNumber)

cadafi
03-01-09, 08:07 AM
Nếu coding trong môi trường khác không phải là Excel thì có lẽ cần. Nếu trong môi trường excel, ta có hai hàm Ceiling và Floor để giải quyết rồi (như hoangvuluan đã nói), không cần viết code chi nữa!

ptm0412
03-01-09, 09:12 AM
Mr Hải mà hỏi, 99% là hỏi trong môi trường khác, không phải hỏi excel

ThuNghi
03-01-09, 09:32 AM
Đúng là chỉ cần 1 dòng:

Private Function RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
RoundDownByBlock = Int(lngNumber&, lngBlockNumber&) * lngBlockNumber&
End Function
Int(lngNumber&, lngBlockNumber&) cú pháp này hình như sai.
Có thể dồn 2 vào 1 luôn, nhưng tôi chưa biết làm thế nào nAlt thông số nào phải = 1 hoặc 0

Private Function RoundByBlock(ByVal lngNumber&, ByVal lngBlockNumber&, Optional nAlt As Byte) As Long
RoundByBlock = Int(lngNumber& / lngBlockNumber&) * lngBlockNumber& + lngBlockNumber& * IIf(nAlt = 0, 0, 1)
End Function

hai2hai
03-01-09, 09:38 AM
Đúng là chỉ cần 1 dòng:

Private Function RoundUpByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
RoundUpByBlock = Int(lngNumber&, lngBlockNumber&) * lngBlockNumber& + lngBlockNumber&
End Function

Private Function RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
RoundDownByBlock = Int(lngNumber&, lngBlockNumber&) * lngBlockNumber&
End Function

Hàm Int chỉ có một đối số thôi bác. Không rõ bác ptm0412 test ở môi trường nào thế? Nhưng hướng của bác là đúng rồi.


Mr Hải mà hỏi, 99% là hỏi trong môi trường khác, không phải hỏi excel
Đúng là h2h chỉ hỏi trong VB chứ ko để ý Excel có Floor với Ceiling (mục tiêu là giống Floor và Ceiling)

Đáp án cực kỳ đơn giản, đúng là chỉ 1 dòng, chỉ có RoundUp thì dùng tới Int(), còn RoundDown thì ko cần tới bất cứ hàm nào (và cũng chỉ có 1 dòng)

ptm0412
03-01-09, 11:24 AM
Nhầm, nhầm chết người thật. Đúng ra là Int(lngNumber&/ lngBlockNumber&)

Vậy:
1. RoundUpByBlock = Int(lngNumber&/ lngBlockNumber&) * lngBlockNumber& + lngBlockNumber&

2. RoundDownByBlock = Int(lngNumber&/ lngBlockNumber&) * lngBlockNumber&

còn RoundDown thì ko cần tới bất cứ hàm nào (và cũng chỉ có 1 dòng)
RoundDown mà không cần Int thì làm sao vậy Mr. Hải?

hai2hai
03-01-09, 12:09 PM
Vậy:
1. RoundUpByBlock = Int(lngNumber&/ lngBlockNumber&) * lngBlockNumber& + lngBlockNumber&



Anh đã thử cho lngNumber = 5, lngBlockNumber = 5 chưa?

Tại sao em đi làm muộn 5 phút (và trong khi Block = 5) mà anh lại cho là em đi làm muộn 10 phút vậy? Làm thế là em ... kiện anh đấy.

Anh thử nhé:

RoundUp: int((number+ block)/block) * block



RoundDown mà không cần Int thì làm sao vậy Mr. Hải?

Anh thử cái này xem sao:


RoundDown: (number \ block) * block

cadafi
03-01-09, 12:47 PM
Cái dấu "\" hình như đã gặp trong bài của bác SA_DQ rồi thì phải, bài về lập hàm trả về số quý của ngày hiện hành. "\" là phép chia lấy phần nguyên phải không anh hai2hai ?

ptm0412
03-01-09, 01:02 PM
Hà, number \ Block thì cũng là Int(Number/ Block)

Còn cái vụ
Anh đã thử cho lngNumber = 5, lngBlockNumber = 5 chưa?

RoundUp: int((number+ block)/block) * block
cũng bị như thường!
đây nhé:
RoundUp = Int((5 + 5)/5) * 5 = 10 !!!!!

RoundDown cũng bị tình trạng đó.

Vầy chắc đúng hơn:

RoundUp = Int(number / (block + 0.01)) * block + block

RoundDown = Number \ (Block + 0.01)

hai2hai
03-01-09, 01:12 PM
Còn cái vụ

RoundUp: int((number+ block)/block) * block
cũng bị như thường!
đây nhé:
RoundUp = Int((5 + 5)/5) * 5 = 10 !!!!!

RoundDown cũng bị tình trạng đó.

Vầy chắc đúng hơn:

RoundUp = Int(number / (block + 0.01)) * block + block

RoundDown = Number \ (Block + 0.01)

Vụ RoundDown:
?(5\5)* 5 = 5 (đi muộn 5 phút thì làm tròn theo Block là 5 là chuẩn rồi anh ơi)

Vụ RoundUp đúng là em cũng ko test trường hợp mà khi em test cái của anh. :)


RoundUp = Int(number / (block + 0.01)) * block + block

Em ko đi muộn phút nào anh lại cho em đi muộn ... 5 phút

Int(0/ (5+ 0.01)) * 5+ 5 = 5 ???

Cái hàm dài dài thì nó như thế này:


Private Function RoundUpByBlock(ByVal lngNumber As Long, ByVal lngBlock As Long) As Long
If Int(lngNumber Mod lngBlock) = 0 Then
RoundUpByBlock = (lngNumber / lngBlock) * lngBlock
Else
RoundUpByBlock = Int((lngNumber / lngBlock) + 1) * lngBlock
End If
End Function

Or Shorter is:


Int((lngNumber / lngBlock) + IIf(Int(lngNumber Mod lngBlock) = 0, 0, 1)) * lngBlock

Test cases:


Debug.Print RoundUpByBlock(0, 5) = 0
Debug.Print RoundUpByBlock(1, 5) = 5
Debug.Print RoundUpByBlock(5, 5) = 5
Debug.Print RoundUpByBlock(6, 5)= 10

Vấn đề là phải thay cái hàm RoundUpByBlock dài dài ở trên thành 1 dòng ngắn như là đã làm với RoundDown (RoundDown = (n\b) * b là chuẩn rồi)

ndu96081631
03-01-09, 01:14 PM
Đáp án cực kỳ đơn giản, đúng là chỉ 1 dòng, chỉ có RoundUp thì dùng tới Int(), còn RoundDown thì ko cần tới bất cứ hàm nào (và cũng chỉ có 1 dòng)
Tôi nghĩ mấu chốt vấn đề nằm ở chổ ta khai báo
RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
Nên khỏi cần làm tròn

hai2hai
03-01-09, 01:18 PM
Tôi nghĩ mấu chốt vấn đề nằm ở chổ ta khai báo
RoundDownByBlock(ByVal lngNumber&, ByVal lngBlockNumber&) As Long
Nên khỏi cần làm tròn

Không phải vấn đề đó. Dĩ nhiên kiểu trở về là kiểu Long rồi. Mấy hàm đó viết ra cho dễ hiểu thôi, giờ mọi người hiểu đầu bài rồi thì ko care tới tên hàm đó nữa.

hai2hai
03-01-09, 02:23 PM
Tạm thời, đây là giải pháp cho ra kết quả đúng:

RoundUpByBlock = Int((lngNumber / lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock
Hoặc:
RoundUpByBlock = ((lngNumber \ lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock

Kết quả:

RoundUpByBlock(0, 5) = 0
RoundUpByBlock(1, 5) = 5
RoundUpByBlock(5, 5) = 5
RoundUpByBlock(6, 5) = 10
RoundUpByBlock(10, 5) = 10
RoundUpByBlock(13, 5) = 15
RoundUpByBlock(15, 5) = 15
RoundUpByBlock(17, 5) = 20

RoundDownByBlock = (lngNumber \ lngBlock ) * lngBlock

Kết quả:

RoundDownByBlock(0, 5) = 0
RoundDownByBlock(4, 5) = 0
RoundDownByBlock(5, 5) = 5
RoundDownByBlock(9, 5) = 5
RoundDownByBlock(10, 5) = 10
RoundDownByBlock(13, 5) = 10
RoundDownByBlock(15, 5) = 15
RoundDownByBlock(19, 5) = 15

Chú ý:
- RoundUp & RoundDown sẽ có cùng kết quả khi lngBlock = 1 (áp dụng trường hợp tính đi muộn, về sớm theo kết quả thực tế chứ ko làm tròn)


Ai có ý tưởng gì mới "đẹp" hơn ko?

hoangvuluan
03-01-09, 02:47 PM
Tạm thời, đây là giải pháp cho ra kết quả đúng:

RoundUpByBlock = Int((lngNumber / lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock
Hoặc:
RoundUpByBlock = ((lngNumber \ lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock

Kết quả:


RoundDownByBlock = (lngNumber \ lngBlock ) * lngBlock

Kết quả:


Chú ý:
- RoundUp & RoundDown sẽ có cùng kết quả khi lngBlock = 1 (áp dụng trường hợp tính đi muộn, về sớm theo kết quả thực tế chứ ko làm tròn)


Ai có ý tưởng gì mới "đẹp" hơn ko?


Cũng chỉ viết lại cho gọn thôi:
RoundUpByBlock = ((lngNumber \ lngBlock) - (lngNumber Mod lngBlock <> 0)) * lngBlock
(Trong VBA, True = -1, khác với trong E: True=1)

cadafi
03-01-09, 02:47 PM
RoundUpByBlock = Int((lngNumber / lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock
Hoặc:
RoundUpByBlock = ((lngNumber \ lngBlock) + IIf(lngNumber Mod lngBlock = 0, 0, 1)) * lngBlock

Kết chỉ có hai điều kiện thì có thể không cần IIF không?
RoundUpByBlock = lngBlock * ((lngNumber \ lngBlock) - ((lngNumber Mod lngBlock) > 0))

hai2hai
03-01-09, 02:50 PM
Đúng. Có vẻ ngắn hơn 1 chút dựa vào True = -1