2009-11-05 2 views
14

Martin Fowler has a Money class, который имеет рутину распределения денег. Эта процедура распределяет деньги в соответствии с определенным списком коэффициентов, не теряя при этом значения при округлении. Он распределяет любое остаточное значение по результатам.Доказательство правильности алгоритма распределения денег Фаулера

Например, 100 долл. США, выделенные «отношениями» (1, 1, 1), будут давать (34 долл. США, 33 долл. США, 33 долл. США).

Вот allocate функция:

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; 
} 

(. Ради этого вопроса, чтобы сделать его проще, я взял на себя смелость заменить типы денег с лонги)

вопрос в том, как я знаю, что это правильно? Все это кажется довольно очевидным, за исключением финальной петли. Я думаю, что для доказательства функция является правильным, было бы достаточно, чтобы доказать, что соотношение справедливо в финале для цикла:

remainder < results.length 

Может кто-нибудь доказать?

+1

Скажите, что вы хотите разбить X-число на Y частей. Напомним, что X% Y всегда

ответ

21

Ключевое понимание состоит в том, что общий остаток равен сумме отдельных остатков при расчете каждого result[i].

Поскольку каждый отдельный остаток является результатом округления, он не более 1. Есть results.length таких остатков, поэтому общий остаток составляет не более results.length.

EDIT: Очевидно, что это не является доказательством без некоторых симпатичных символов, так вот некоторые ...
alt text

+0

Да, это была та часть, которую мне не хватало. Спасибо. –

+0

+1. Обратите внимание, что последний '' 'должен быть' = ', так как' \ sum_ {i = 1}^k 1 = k'. – Stephan202

+0

Так оно и должно - это то, что я получаю, чтобы свернуть его на меньшее количество строк. Первоначально он сказал 'остаток stevemegson

0

Я бы сказал, что это неправильно, потому что какое-то любопытное отношение может привести к тому, что остаток будет больше, чем количество результатов. Поэтому я предлагаю results[i % results.length].amount++;.

Редактировать: Я снимаю свой ответ. С longs нет любопытного отношения и с плавающей точкой по модулю не помогает

+0

Я бы не согласился. Этот любопытный рацион математически невозможно. –

+0

Для набора целых чисел нет никакого «любопытного» отношения. –

+0

Да, для длинных нет ни одного. Но ОП сказал, что длинные используются только для простоты в этом примере. И все мы знаем, что нельзя сравнивать двойные значения без коэффициента ошибок.Поэтому в компьютерной программе могут возникать любопытные отношения, из-за этого коэффициента ошибок – DaClown

1

Не требуется никаких доказательств.

Базовые суммы распределяются простым делением, округлением вниз. Таким образом, выделенная сумма всегда будет меньше или равна сумме.

Remainder содержит нераспределенную сумму. Который всегда будет целым числом меньше, чем «i». Поэтому он просто дает каждому получателю 1, пока деньги не исчезнут.

+1

, что выделенная сумма <= общая сумма понятна, но почему она гарантирована <чем количество коэффициентов? –

+0

Его не <=, его просто <('меньше'). Если выделенная сумма равна, то деление будет идеально разделять результат, не так ли? 4% 2 не 1 с напоминанием 2 и никогда не будет. –

+1

Если вы выделите $ 99 с коэффициентами (1,1,1), первый шаг алгоритма будет выделять 99, поэтому шаг, выделенный первым шагом, действительно может быть равен общей сумме. Но это не мой вопрос. Я не понимаю, почему нераспределенная сумма (на первом шаге) меньше, чем #ratios. –

0

Простой

просто использовать тот факт, что

а = пол (а/b) * b + (a% b)

 Смежные вопросы

  • Нет связанных вопросов^_^