2009-11-05 28 views
14

Martin Fowler has a Money class có thói quen phân bổ tiền. Quy trình này phân bổ tiền theo danh sách các tỷ lệ đã cho mà không làm mất bất kỳ giá trị nào thông qua làm tròn. Nó lây lan bất kỳ giá trị còn lại trên kết quả.Chứng minh rằng thuật toán phân bổ tiền của Fowler là chính xác

Ví dụ: $ 100 được phân bổ theo "tỷ lệ" (1, 1, 1) sẽ mang lại ($ 34, $ 33, $ 33).

Đây là allocate chức năng:

public long[] allocate(long amount, long[] ratios) { 
    long total = 0; 
    for (int i = 0; i < ratios.length; i++) total += ratios[i]; 

    long remainder = amount; 
    long[] results = new long[ratios.length]; 
    for (int i = 0; i < results.length; i++) { 
     results[i] = amount * ratios[i]/total; 
     remainder -= results[i]; 
    } 

    for (int i = 0; i < remainder; i++) { 
     results[i]++; 
    } 

    return results; 
} 

(. Vì mục đích của câu hỏi này, để làm cho nó đơn giản hơn, tôi đã lấy sự tự do thay thế các loại tiền với chờ đợi)

Các câu hỏi là, làm thế nào để tôi biết nó là chính xác? Tất cả có vẻ khá rõ ràng, ngoại trừ vòng lặp cuối cùng. Tôi nghĩ rằng để chứng minh chức năng là chính xác, sẽ đủ để chứng minh rằng mối quan hệ sau đây là đúng trong vòng lặp cuối cùng:

remainder < results.length 

Có ai chứng minh điều đó không?

+1

Giả sử bạn muốn chia số X thành các phần Y. Lời nhắc là X% Y luôn là

Trả lời

21

Thông tin chi tiết chính là tổng số tiền còn lại bằng tổng của các khoản tiền cá nhân khi tính mỗi result[i].

Vì mỗi phần còn lại là kết quả làm tròn xuống, tối đa là 1. Có results.length số dư còn lại, vì vậy tổng số dư còn lại tối đa là results.length.

EDIT: Rõ ràng nó không phải là một bằng chứng mà không cần một số biểu tượng khá, vì vậy đây là một số ...
alt text

+0

vâng, đó là phần tôi đã mất tích. Cám ơn. –

+0

+1. Lưu ý rằng '' 'cuối cùng phải là' = ', vì' \ sum_ {i = 1}^k 1 = k'. – Stephan202

+0

Vì vậy, nó nên - đó là những gì tôi nhận được để thu gọn nó vào ít dòng hơn. Ban đầu nó đã nói 'phần còn lại stevemegson

0

Tôi muốn nói điều đó không chính xác vì một số tỷ lệ tò mò có thể khiến số dư còn lại lớn hơn số lượng kết quả. Do đó tôi đề nghị results[i % results.length].amount++;.

Chỉnh sửa: Tôi rút câu trả lời của mình. Với thời gian dài không có tỷ lệ tò mò và với điểm nổi modulo không giúp

+0

Tôi sẽ phải không đồng ý. Khẩu phần tò mò này là không thể toán học. –

+0

Đối với tập hợp các số nguyên, không có tỷ lệ "tò mò". –

+0

Có, trong thời gian dài không có. Nhưng OP cho biết thời gian dài chỉ được sử dụng vì mục đích đơn giản trong ví dụ này. Và tất cả chúng ta đều biết rằng người ta không nên so sánh các giá trị kép mà không có một tỷ lệ lỗi.Do đó trong một chương trình máy tính tỷ lệ tò mò có thể xảy ra, vì tỷ lệ lỗi này – DaClown

1

Không cần bằng chứng.

Số tiền cơ sở được phân bổ theo phân chia đơn giản, làm tròn xuống. Vì vậy, số tiền được phân bổ sẽ luôn nhỏ hơn hoặc bằng tổng số.

Phần còn lại chứa số tiền chưa phân bổ. Mà sẽ luôn luôn là một số nguyên nhỏ hơn 'i'. Vì vậy, ông chỉ đơn giản là cung cấp cho mỗi người nhận 1 cho đến khi tiền đã biến mất.

+1

rằng số tiền được phân bổ là <= tổng số là rõ ràng, nhưng tại sao nó được đảm bảo là

+0

Không phải của nó <=, nó chỉ <('ít hơn'). Nếu số tiền được phân bổ bằng nhau thì độ lệch sẽ phân chia kết quả một cách hoàn hảo, đúng không? 4% 2 không phải là 1 với lời nhắc 2 và sẽ không bao giờ được. –

+1

Nếu bạn phân bổ 99 đô la với tỷ lệ (1,1,1), bước đầu tiên của thuật toán sẽ phân bổ 99, do đó, bước được phân bổ bởi bước đầu tiên thực sự có thể bằng tổng số tiền. Nhưng đó không phải là câu hỏi của tôi. Những gì tôi không hiểu là lý do tại sao số tiền chưa phân bổ (trong bước đầu tiên) nhỏ hơn #ratios. –

0

Simple

chỉ cần sử dụng thực tế là

a = sàn (một/b) * b + (a% b)

Các vấn đề liên quan