2009-11-05 3 views
14

Martin Fowler has a Money class che ha una routine di allocazione del denaro. Questa routine assegna denaro in base a una determinata lista di rapporti senza perdere alcun valore attraverso l'arrotondamento. Distribuisce qualsiasi valore residuo sui risultati.Prova che l'algoritmo di allocazione del denaro di Fowler è corretto

Ad esempio, $ 100 assegnati dai "ratios" (1, 1, 1) produrranno ($ 34, $ 33, $ 33).

Ecco la funzione 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; 
} 

(. Per il bene di questa domanda, per rendere più semplice, mi sono preso la libertà di sostituire i tipi di denaro con anela)

Il la domanda è, come faccio a sapere che è corretto? Sembra tutto molto evidente tranne che per il ciclo finale. Credo che per dimostrare la funzione è corretta, sarebbe sufficiente a dimostrare che la seguente relazione è vera nella finale per-loop:

remainder < results.length 

Qualcuno può dimostrare che?

+1

Dire di voler dividere il numero X in parti Y. Il promemoria è X% Y che è sempre

risposta

21

L'intuizione chiave è che il resto totale è uguale alla somma dei singoli rimanenti durante il calcolo di ogni result[i].

Poiché ogni individuo resto è il risultato di arrotondamento per difetto, è al massimo 1. Esistono results.length tali residui, quindi il rimanente totale è al massimo results.length.

EDIT: Ovviamente non è una prova, senza alcune belle simboli, quindi ecco alcuni ...
alt text

+0

sì, quella era la parte che mi mancava. Grazie. –

+0

+1. Si noti che l'ultimo '<' dovrebbe essere '=', poiché '\ sum_ {i = 1}^k 1 = k'. – Stephan202

+0

Quindi dovrebbe - questo è quello che ottengo per crollarlo su meno linee. Originariamente aveva detto 'remainder stevemegson

0

Direi che non è corretto perché qualche rapporto curioso potrebbe causare un resto maggiore del numero di risultati. Pertanto suggerisco results[i % results.length].amount++;.

Modifica: ritiro la risposta. Con i lunghi non c'è un rapporto curioso e con il modulo in virgola mobile non aiuta

+0

Non sarei d'accordo. Questa curiosa razione è matematicamente impossibile. –

+0

Per l'insieme di numeri interi non esiste un rapporto "curioso". –

+0

Sì, a lungo non ce n'è uno. Ma l'OP diceva che i long sono usati solo per semplicità in questo esempio. E sappiamo tutti che non si dovrebbero confrontare i valori doppi senza un rapporto di errore.Pertanto, in un programma per computer possono verificarsi strani rapporti, a causa di questo rapporto di errore – DaClown

1

Nessuna prova richiesta.

Gli importi di base sono assegnati per divisione semplice, arrotondando per difetto. Quindi l'importo assegnato sarà sempre inferiore o uguale al totale.

Il resto contiene la quantità non assegnata. Quale sarà sempre un numero intero inferiore a "i". Quindi dà semplicemente ad ogni ricevitore 1 fino a che i soldi non sono andati.

+1

che la quantità allocata è <= il totale è chiaro, ma perché è garantito

+0

Its not <=, its just <('less'). Se la somma assegnata è uguale, la deviazione dividerebbe il risultato perfettamente, vero? 4% 2 non è 1 con promemoria 2 e non lo sarà mai. –

+1

Se si assegna $ 99 con rapporti (1,1,1), il primo passo dell'algoritmo assegnerà 99, quindi il passo assegnato dal primo passo può effettivamente essere uguale all'importo totale. Ma non è davvero la mia domanda. Quello che non capisco è perché la quantità non assegnata (nel primo passo) è inferiore a #ratios. –

0

semplice

basta usare fatto che

a = piano (un/b) * b + (a% b)

Problemi correlati