2015-11-30 22 views
19

我正在製作一個遊戲,其中包括$ 10,$ 5,$ 3和$ 1的硬幣面值。玩家可以在他的庫存中擁有0種或更多種類型的貨幣,最多可以有15種硬幣。我想弄清楚如何正確選擇硬幣,以便以最小的變化量作爲回報。起初我認爲這個問題很容易解決,但現在我無法繞開它。選擇最少或沒有變化的硬幣

下面是兩個例子說明情況進一步:

實施例1:

在使用者攜帶這些硬幣:$ 5,$ 3,$ 3,$ 3,$ 1,$ 1,$ 1,$ 1和想要以12美元購買一件物品。解決方案是用5美元,3美元,3美元,1美元支付,並且不改變。

實施例2:

用戶不具有任何$ 1枚硬幣,並正在執行$ 5,$ 3,$ 3,$ 3,$ 3一件物品是以12美元的價格購買的,所以他們用5美元,3美元,3美元和3美元支付,並且2美元的更換被退還。

由於我們先選擇較大的硬幣,但我無法弄清楚的是如何知道玩家庫存中是否有足夠的低價值硬幣(本例中爲1美元)以適應示例1,並且如果不存在「噸足夠使用多個較高值的硬幣的如實施例2

的另一個問題是在下面的例子中所見,雖然我很高興剛開上面的兩個例子的工作:

實施例3 : 用戶攜帶這些硬幣:$ 5,$ 3,$ 3,$ 3。玩家購買6美元的東西。最好使用3美元和3美元,並且不返回任何更改,而是使用5美元和3美元,並給予2美元的更改。

我相信前兩個例子可以使用遞歸和貪婪算法的變體來解決。

對於賞金獎:

我已經加入我自己的答案下面是一個臨時解決方案現在。但是,我喜歡下面的Llama先生的方法(請參閱他參考的鏈接),並且希望找到一個滿足此條件的PHP示例。我相信這種方法不需要遞歸併使用記憶。

如果有多種選擇的最小變化量,那麼我希望領帶被給予最少量的硬幣支付。

+0

我想只是使用簡單的動態編程是好的 – throwit

+0

嗯,我試圖解決這個問題,因爲它看起來很有趣。我寫了一個算法,應該爲你工作,因爲我不想安裝PHP服務器,我做了它在JS。你可以將其轉換爲PHP。讓我知道如果它確定。我可以給你的代碼。 –

+0

@HarryBomrah,是的,絕對會有很大的幫助。 – kojow7

回答

0

這個答案是基於גלעד-ברקן的答案。我按照他的要求在這裏發佈。儘管沒有一個答案是我正在尋找的答案,但我發現這是發佈的最佳選擇。下面是我目前使用的改進算法:

<?php 

function leastChange($inventory, $price){ 

    //NOTE: Hard coded these in the function for my purposes, but $coin value can be passed as a parameter for a more general-purpose algorithm 
    $num_coin_types = 4; 
    $coin_value = [10,5,3,1]; 

    $have = 0; 
    for ($i=0; $i < $num_coin_types; $i++){ 
      $have += $inventory[$i] * $coin_value[$i]; 
    } 

    //NOTE: Check to see if you have enough money to make this purchase 
    if ($price > $have){ 
      $error = ["error", "Insufficient Funds"]; 
      return $error; 
    } 

    $stack = [[0,$price,$have,[]]]; 
    $best = [-max($coin_value),[]]; 

    while (!empty($stack)){ 

      // each stack call traverses a different set of parameters 
      $parameters = array_pop($stack); 

      $i = $parameters[0]; 
      $owed = $parameters[1]; 
      $have = $parameters[2]; 
      $result = $parameters[3]; 

      if ($owed <= 0){ 
        //NOTE: check for new option with least change OR if same as previous option check which uses the least coins paid 
        if ($owed > $best[0] || ($owed == $best[0] && (array_sum($result) < array_sum($best[1])))){ 

          //NOTE: add extra zeros to end if needed 
          while (count($result) < 4){ 
            $result[] = 0; 
          } 
          $best = [$owed,$result]; 
        } 
        continue; 
      } 

      // skip if we have none of this coin 
      if ($inventory[$i] == 0){ 
        $result[] = 0; 
        $stack[] = [$i + 1,$owed,$have,$result]; 
        continue; 
      } 

      // minimum needed of this coin 
      $need = $owed - $have + $inventory[$i] * $coin_value[$i]; 

      if ($need < 0){ 
        $min = 0; 
      } else { 
        $min = ceil($need/$coin_value[$i]); 
      } 

      // add to stack 
      for ($j=$min; $j<=$inventory[$i]; $j++){ 
        $stack[] = [$i + 1,$owed - $j * $coin_value[$i],$have - $inventory[$i] * $coin_value[$i],array_merge($result,[$j])]; 
        if ($owed - $j * $coin_value[$i] < 0){ 
          break; 
        } 
      } 
    } 

    return $best; 
} 

這裏是我的測試代碼:

$start = microtime(true); 

$inventory = [0,1,3,4]; 
$price = 12; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 



$inventory = [0,1,4,0]; 
$price = 12; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 

$inventory = [0,1,4,0]; 
$price = 6; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 


$inventory = [0,1,4,0]; 
$price = 7; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 


$inventory = [1,3,3,10]; 
$price=39; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 

$inventory = [1,3,3,10]; 
$price=45; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 

//stress test 
$inventory = [25,25,25,1]; 
$price=449; 
echo "\n"; 
echo json_encode(leastChange($inventory,$price)); 
echo "\n"; 

$time_elapsed = microtime(true) - $start; 
echo "\n Time taken: $time_elapsed \n"; 

結果:

[0,[0,1,2,1]] 

[0,[0,0,4,0]] 

[0,[0,0,2,0]] 

[-1,[0,1,1,0]] 

[0,[1,3,3,5]] 

["error","Insufficient Funds"] 

[-1,[25,25,25,0]] 

Time taken: 0.0046839714050293 

當然,時間是微秒,因此它在幾分之一秒內執行!

14

的問題可被定義爲:

Return a subset of items where the sum is closest to x, but >= x. 

此問題被稱爲子集和問題。這是NP完整的。你不會找到一個運行在僞多項式時間的完美算法,只有不完美的啓發式算法。

但是,如果硬幣數量非常少,那麼對解決方案空間進行徹底搜索肯定會奏效。

如果硬幣的個數越多,那麼你應該看看維基百科的概述:https://en.wikipedia.org/wiki/Subset_sum_problem#Polynomial_time_approximate_algorithm

+2

有*是一個完美的算法。這取決於你願意花費多少時間來找到你的目標。你可以用一種天真的方法來嘗試所有可能的硬幣組合,這將被認爲是「完美」的,因爲如果它在那裏它不會錯過答案。然而,有更快的方法:http://stackoverflow.com/a/19572277/477563 –

+0

這是在現實生活中,我尋找最容易實施這種相當有效的問題。 IIRC中,「貪婪」算法(剩餘最大的賬單<=餘額)是大約80%的NP完全問題的最佳解決方案。 OP還可以調整票據面額,因此每個面額都是下一個最低面值的精確倍數,這將保證始終使用貪婪算法取得成功......在他設計的遊戲世界中似乎是合理的。 –

10

我有一個類似的問題,除了沒有被允許走了過來,組合不得不留在目標量下。最後,我使用了this answer中介紹的動態方法。你也應該可以使用它。

它是這樣的:

  1. 開始使用由單個空元素的列表。
  2. 對於列表中的每個條目...
    1. 複製的條目,並添加到它的第一個硬幣(硬幣沒有價值!),它不包含。
    2. 將新元素存儲在原始列表中當且僅當*其新和值不存在於列表中時。
  3. 重複步驟2,直到您在何處沒有新的元素添加到列表中
  4. 迭代結果列表,並保持最佳的組合了一通(使用標準)

*:我們可以做這個優化是因爲我們並不特別在意哪個硬幣用於組合,只有硬幣收集的總和值。

如果您使用總和值作爲關鍵,上述算法可以優化一點。

+0

謝謝。我發佈了一個新的解決方案,我已經提出了一個可能的答案。這是時間效率,但並沒有完全解決我的例子3. – kojow7

+0

@ kojow7 - 怎麼不?在最後一步,第4部分中,保持大於或等於您的目標的最低結果。你把每個硬幣當作一個獨特的對象,是嗎? (在步驟2.1中,如果沒有包含該特定硬幣,則不應該添加它,而不是該硬幣的*值*) –

+0

對不起,我的意思是我提出的解決方案並不能解決我的示例3.我仍然試圖完全理解您的解決方案如何工作,並且正在考慮是否需要遞歸,那麼它可能不是時間效率高。 – kojow7

1

我想出了以下解決方案。如果其他人可以對我進行評論,我將不勝感激。

<?php 

$coin_value = array(10,5,3,1); 
$inventory = array(1,2,0,2); 
$price = 17; 

for ($i = 3; $i >= 0; $i--){ 

     $btotal = 0; 
     $barray = array(); 

     for ($j = 0; $j < 4; $j++){ 
       $remaining = $price - $btotal; 
       $to_add = floor($remaining/$coin_value[$j]); 

       if ($i != 3 && $i == $j){ 
         $to_add++; 
       } 

       if ($inventory[$j] < $to_add){ 
         $to_add = $inventory[$j]; 
       } 

       $btotal += $to_add * $coin_value[$j]; 

       for ($k = 0; $k < $to_add; $k++){ 
         $barray[] = $coin_value[$j]; 
       } 

       if ($btotal >= $price) 
         break 2; //warning: breaks out of outer loop 

     } 
} 

$change_due = $btotal - $price; 

print_r($barray); 

echo "Change due: \$$change_due\n"; 

?> 

它涵蓋了示例1和2中的我原來的問題,但沒有涉及到例如3。但是,我認爲它會爲現在做的,除非有人能想出更好的解決方案。我決定不使用遞歸,因爲它似乎需要太多時間。

1

您可以使用堆棧來枚舉有效組合。以下版本使用一個小的優化,計算是否需要當前面額的最小值。如果存在任何可能會被記憶化限制的變化組合,則返回多於一個的最小變化組合;如果當前面額能夠以零變化完成組合,則還可以增加提前退出。我希望扼要地註釋代碼是不言自明的(讓我知道,如果你想進一步的解釋):

function leastChange($coin_value,$inventory,$price){ 
    $n = count($inventory); 
    $have = 0; 
    for ($i=0; $i<$n; $i++){ 
    $have += $inventory[$i] * $coin_value[$i]; 
    } 

    $stack = [[0,$price,$have,[]]]; 
    $best = [-max($coin_value),[]]; 

    while (!empty($stack)){ 

    // each stack call traverses a different set of parameters 
    $parameters = array_pop($stack); 
    $i = $parameters[0]; 
    $owed = $parameters[1]; 
    $have = $parameters[2]; 
    $result = $parameters[3]; 

    // base case 
    if ($owed <= 0){ 
     if ($owed > $best[0]){ 
     $best = [$owed,$result]; 
     } else if ($owed == $best[0]){ 

     // here you can add a test for a smaller number of coins 

     $best[] = $result; 
     } 
     continue; 
    } 

    // skip if we have none of this coin 
    if ($inventory[$i] == 0){ 
     $result[] = 0; 
     $stack[] = [$i + 1,$owed,$have,$result]; 
     continue; 
    } 

    // minimum needed of this coin 
    $need = $owed - $have + $inventory[$i] * $coin_value[$i]; 

    if ($need < 0){ 
     $min = 0; 
    } else { 
     $min = ceil($need/$coin_value[$i]); 
    } 

    // add to stack 
    for ($j=$min; $j<=$inventory[$i]; $j++){ 
     $stack[] = [$i + 1,$owed - $j * $coin_value[$i],$have - $inventory[$i] * $coin_value[$i],array_merge($result,[$j])]; 
     if ($owed - $j * $coin_value[$i] < 0){ 
     break; 
     } 
    } 
    } 

    return $best; 
} 

輸出:

$coin_value = [10,5,3,1]; 
$inventory = [0,1,3,4]; 
$price = 12; 

echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,1,2,1],[0,1,1,4],[0,0,3,3]] 

$coin_value = [10,5,3,1]; 
$inventory = [0,1,4,0]; 
$price = 12; 

echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,0,4]] 

$coin_value = [10,5,3,1]; 
$inventory = [0,1,3,0]; 
$price = 6; 

echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,0,2]] 

$coin_value = [10,5,3,1]; 
$inventory = [0,1,3,0]; 
$price = 7; 

echo json_encode(leastChange($coin_value,$inventory,$price)); // [-1,[0,1,1]] 

更新:

既然你也有興趣在最低的號碼硬幣,我認爲memoization只能工作,如果我們能保證更好的可能性不會被跳過。如果我們先用最大的硬幣進行我們的深度優先搜索,我認爲可以做到這一點。如果我們已經使用較大的硬幣達到相同的金額,則繼續當前線程沒有意義。使降面額大小的順序排序確定輸入庫存呈現硬幣,並添加/更改如下:

// maximum needed of this coin 
$max = min($inventory[$i],ceil($owed/$inventory[$i])); 

// add to stack 
for ($j=$max; $j>=$min; $j--){ 
+0

如果有多個選項會導致相同數量的更改,我如何才能返回使用最少數量硬幣的選項? – kojow7

+0

@ kojow7感謝您的評論。你在問我的代碼嗎?如果你看一下注釋爲「base case」的部分,你會發現如果程序看到一個較低的變化組合,它會覆蓋'best'變量,但是如果它有相同的變化量,它會將組合添加到名單。所以最終它會輸出儘可能多的最低變化組合。 (對於你的參數,我不會擔心速度;但無論如何,我在答案中包含了一個更優化解決方案的建議。) –

+0

@ kojow7程序通過查找最小的'owed'參數小於或等於零。 「欠」參數顯示了在組合構建時需要付出多少費用;一旦'欠'低於零,這意味着你會得到改變...... –

0

我能夠做出涵蓋了3個例子張貼在你的問題的解決方案。並且總是用盡可能少的硬幣來改變。

我所做的測試似乎執行得非常快。

我在這裏張貼代碼:

<?php 

//Example values 
$coin_value = array(10,5,3,1); 
$inventory = array(5,4,3,0); 
$price = 29; 

//Initialize counters 
$btotal = 0; 
$barray = array(0,0,0,0); 

//Get the sum of coins 
$total_coins = array_sum($inventory); 

function check_availability($i) { 
    global $inventory, $barray; 
    $a = $inventory[$i]; 
    $b = $barray[$i]; 
    $the_diff = $a - $b; 
    return $the_diff != 0; 
} 

/* 
* Checks the lower currency available 
* Returns index for arrays, or -1 if none available 
*/ 
function check_lower_available() { 
    for ($i = 3; $i >= 0; $i--) { 
     if (check_availability($i)) { 
      return $i; 
     } 
    } 
    return -1; 
} 

for($i=0;$i<4;$i++) { 
    while(check_availability($i) && ($btotal + $coin_value[$i]) <= $price) { 
     $btotal += $coin_value[$i]; 
     $barray[$i]++; 
    } 
} 

if($price != $btotal) { 
    $buf = check_lower_available(); 
    for ($i = $buf; $i >= 0; $i--) { 
     if (check_availability($i) && ($btotal + $coin_value[$i]) > $price) { 
      $btotal += $coin_value[$i]; 
      $barray[$i]++; 
      break; 
     } 
    } 
} 

// Time to pay 
$bchange = 0; 
$barray_change = array(0,0,0,0); 

if ($price > $btotal) { 
    echo "You have not enough money."; 
} 
else { 
    $pay_msg = "You paid $".$btotal."\n\n"; 
    $pay_msg.= "You used ".$barray[0]." coins of $10\n"; 
    $pay_msg.= "You used ".$barray[1]." coins of $5\n"; 
    $pay_msg.= "You used ".$barray[2]." coins of $3\n"; 
    $pay_msg.= "You used ".$barray[3]." coins of $1\n\n\n"; 
    // Time to give change 
    $the_diff = $btotal - $price; 
    if (!empty($the_diff)) { 
     for ($i = 0; $i < 4; $i++) { 
      while($the_diff >= $coin_value[$i]) { 
       $bchange += $coin_value[$i]; 
       $barray_change[$i]++; 
       $the_diff -= $coin_value[$i]; 
      } 
     } 

     $check_sum = array_sum($inventory) - array_sum($barray); 
     $check_sum+= array_sum($barray_change); 
     $msg = ""; 
     if ($check_sum < 15) { 
      $change_msg = "Your change: $".$bchange."\n\n"; 
      $change_msg.= "You received ".$barray_change[0]." coins of $10\n"; 
      $change_msg.= "You received ".$barray_change[1]." coins of $5\n"; 
      $change_msg.= "You received ".$barray_change[2]." coins of $3\n"; 
      $change_msg.= "You received ".$barray_change[3]." coins of $1\n\n"; 
      $msg = $pay_msg.$change_msg; 
     } 
     else { 
      $msg = "You have not enough space to hold the change.\n"; 
      $msg.= "Buy cancelled.\n"; 
     } 
    } 
    else { 
     $msg = $pay_msg."You do not need change\n"; 
    } 
    if ($check_sum < 15) { 
     for ($i = 0; $i < 4; $i++) { 
      $inventory[$i] -= $barray[$i]; 
      $total_coins-= $barray[$i]; 
     } 
     for ($i = 0; $i < 4; $i++) { 
      $inventory[$i] += $barray_change[$i]; 
      $total_coins+= $barray[$i]; 
     } 
    } 
    echo $msg; 
    echo "Now you have:\n"; 
    echo $inventory[0]." coins of $10\n"; 
    echo $inventory[1]." coins of $5\n"; 
    echo $inventory[2]." coins of $3\n"; 
    echo $inventory[3]." coins of $1\n"; 
} 
+0

對不起,這不適用於我的第三個示例。我也建議不要使用全局變量。 – kojow7

0

這是我的解決方案,我不知道是怎麼有效的,但它的作品,我打開的建議。

<?php 

    $player=array(0,3,1,0);//how much coins you have 
    $player_copy=$player; 
    $coin_count=array(0,0,0,0);//memorize which coins you gave 
    $coin_value=array(1,3,5,10); 
    $price=6;  //price of item 
    $price_copy=$price; 
    $z=3; 
    $change=array(-1,-1,-1,-1,-1); //memorise possible changes you can get 
    $k=0; 
    $flag=0; 

label1: for($j=3;$j>=0;$j--){ 
      $coin_count[$j]=0; 
      $player[$j]=$player_copy[$j]; 
     } 
     for($j=$z;$j>=0;$j--){ 
      while(($price>0) && 1<=$player[$j]){ 
       $price-=$coin_value[$j]; 
       $player[$j]--; 
       $coin_count[$j]++; 
      } 
     } 
     $change[$k++]=$price; 
     if($price!=0){ 
       for($j=$z;$j>=0;$j--) 
        if($price_copy>$coin_value[$j]){ 
         $z=$j-1; 
         $price=$price_copy; 
         goto label1; 
        } 
       $flag=1; 
     } 
    //find minimum change 
     $minv=$change[0]; 

     for($i=1;$change[$i]>=0 and $i<4;$i++) 
      if($change[$i]>$minv) 
       $minv=$change[$i]; 
     $i; 
    //when you find minimum change find which coins you have used 
     for($i=0;$i<4;$i++) 
      if($change[$i]==$minv && $flag==1){ 
       $flag=2; 
       for($j=3;$j>=0;$j--){//reset coin_count and player budget 
        $coin_count[$j]=0; 
        $player[$j]=$player_copy[$j]; 
       } 
       for($j=3-($i%2)-1;$j>=0;$j--){ 
        while(($price>0) && 1<=$player[$j]){ 
         $price-=$coin_value[$j]; 
         $player[$j]--; 
         $coin_count[$j]++; 
        } 
        } 
       } 
//prints result 
     for($j=0;$j<4;$j++) 
      printf("%d x %d\n",$coin_count[$j],$coin_value[$j]); 
     printf("change: %d\n",$minv); 
?> 
0

我不知道PHP,所以我在Java中試過它。我希望這很重要,因爲它的算法很重要。

我的代碼如下:

package stackoverflow.changecalculator; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

public class ChangeCalculator 
{ 
    List<Integer> coinsInTil = new ArrayList<>(); 

    public void setCoinsInTil(List<Integer> coinsInTil) 
    { 
     this.coinsInTil = coinsInTil; 
    } 

    public Map<String, List> getPaymentDetailsFromCoinsAvailable(final int amountOwed, List<Integer> inPocketCoins) 
    { 
     List<Integer> paid = new ArrayList<>(); 
     int remaining = amountOwed; 

     // Check starting with the largest coin. 
     for (Integer coin : inPocketCoins) 
      if (remaining > 0 && (remaining - coin) >= 0) { 
        paid.add(coin); 
        remaining = remaining - coin; 
      } 

     ProcessAlternative processAlternative = new ProcessAlternative(amountOwed, inPocketCoins, paid, remaining).invoke(); 
     paid = processAlternative.getPaid(); 
     remaining = processAlternative.getRemaining(); 

     removeUsedCoinsFromPocket(inPocketCoins, paid); 
     int changeOwed = payTheRestWithNonExactAmount(inPocketCoins, paid, remaining); 
     List<Integer> change = calculateChangeOwed(changeOwed); 

     Map<String, List> result = new HashMap<>(); 
     result.put("paid", paid); 
     result.put("change", change); 
     return result; 
    } 

    private void removeUsedCoinsFromPocket(List<Integer> inPocketCoins, List<Integer> paid) 
    { 
     for (int i = 0; i < inPocketCoins.size(); i++) { 
      Integer coin = inPocketCoins.get(i); 
      if (paid.contains(coin)) 
       inPocketCoins.remove(i); 
     } 
    } 

    private List<Integer> calculateChangeOwed(int changeOwed) 
    { 
     List<Integer> change = new ArrayList<>(); 
     if (changeOwed < 0) { 
      for (Integer coin : coinsInTil) { 
       if (coin + changeOwed == 0) { 
        change.add(coin); 
        changeOwed = changeOwed + coin; 
       } 
      } 
     } 
     return change; 
    } 

    private int payTheRestWithNonExactAmount(List<Integer> inPocketCoins, List<Integer> paid, int remaining) 
    { 
     if (remaining > 0) { 
      for (int coin : inPocketCoins) { 
       while (remaining > 0) { 
        paid.add(coin); 
        remaining = remaining - coin; 
       } 
      } 
     } 
     return remaining; 
    } 
} 

的ProcessAlternative類處理情況下,最大的硬幣不允許我們得到那裏是要返回沒有變化,所以我們嘗試另一種情況。

package stackoverflow.changecalculator; 

import java.util.ArrayList; 
import java.util.List; 

// if any remaining, check if we can pay with smaller coins first. 
class ProcessAlternative 
{ 
    private int amountOwed; 
    private List<Integer> inPocketCoins; 
    private List<Integer> paid; 
    private int remaining; 

    public ProcessAlternative(int amountOwed, List<Integer> inPocketCoins, List<Integer> paid, int remaining) 
    { 
     this.amountOwed = amountOwed; 
     this.inPocketCoins = inPocketCoins; 
     this.paid = paid; 
     this.remaining = remaining; 
    } 

    public List<Integer> getPaid() 
    { 
     return paid; 
    } 

    public int getRemaining() 
    { 
     return remaining; 
    } 

    public ProcessAlternative invoke() 
    { 
     List<Integer> alternative = new ArrayList<>(); 
     int altRemaining = amountOwed; 
     if (remaining > 0) { 
      for (Integer coin : inPocketCoins) 
       if (altRemaining > 0 && factorsOfAmountOwed(amountOwed).contains(coin)) { 
        alternative.add(coin); 
        altRemaining = altRemaining - coin; 
       } 
      // if alternative doesn't require change, use it. 
      if (altRemaining == 0) { 
       paid = alternative; 
       remaining = altRemaining; 
      } 
     } 
     return this; 
    } 

    private ArrayList<Integer> factorsOfAmountOwed(int num) 
    { 
     ArrayList<Integer> aux = new ArrayList<>(); 
     for (int i = 1; i <= num/2; i++) 
      if ((num % i) == 0) 
       aux.add(i); 
     return aux; 
    } 
} 

我在它的工作通過做試驗中,例如如圖1所示,然後例如2,最後轉移到實施例3的方法,在這裏加入的替代位和原始測試硬幣替代返回0變化因此我將輸入的數量更新爲15而不是12,因此它會計算所需的更改。

測試如下:

package stackoverflow.changecalculator; 

import org.junit.Before; 
import org.junit.Test; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map; 

import static org.junit.Assert.assertEquals; 
import static org.junit.Assert.assertTrue; 

public class ChangeCalculatorTest 
{ 
    public static final int FIFTY_PENCE = 0; 
    public static final int TWENTY_PENCE = 1; 
    public static final int TEN_PENCE = 2; 
    public static final int FIVE_PENCE = 3; 
    public static final int TWO_PENCE = 4; 
    public static final int PENNY = 5; 

    public ChangeCalculator calculator; 

    @Before 
    public void setUp() throws Exception 
    { 
     calculator = new ChangeCalculator(); 
     List<Integer> inTil = new ArrayList<>(); 
     inTil.add(FIFTY_PENCE); 
     inTil.add(TWENTY_PENCE); 
     inTil.add(TEN_PENCE); 
     inTil.add(FIVE_PENCE); 
     inTil.add(TWO_PENCE); 
     inTil.add(PENNY); 
     calculator.setCoinsInTil(inTil); 
    } 

    @Test 
    public void whenHaveExactAmount_thenNoChange() throws Exception 
    { 
     // $5, $3, $3, $3, $1, $1, $1, $1 
     List<Integer> inPocket = new ArrayList<>(); 
     inPocket.add(5); 
     inPocket.add(3); 
     inPocket.add(3); 
     inPocket.add(3); 
     inPocket.add(1); 
     inPocket.add(1); 
     inPocket.add(1); 
     inPocket.add(1); 

     Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(12, inPocket); 

     List change = result.get("change"); 
     assertTrue(change.size() == 0); 
     List paid = result.get("paid"); 
     List<Integer> expected = new ArrayList<>(); 
     expected.add(5); 
     expected.add(3); 
     expected.add(3); 
     expected.add(1); 
     assertEquals(expected, paid); 
    } 

    @Test 
    public void whenDoNotHaveExactAmount_thenChangeReturned() throws Exception { 
     // $5, $3, $3, $3, $3 
     List<Integer> inPocket = new ArrayList<>(); 
     inPocket.add(5); 
     inPocket.add(3); 
     inPocket.add(3); 
     inPocket.add(3); 
     inPocket.add(3); 

     Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(15, inPocket); 

     List change = result.get("change"); 
     Object actual = change.get(0); 
     assertEquals(2, actual); 
     List paid = result.get("paid"); 
     List<Integer> expected = new ArrayList<>(); 
     expected.add(5); 
     expected.add(3); 
     expected.add(3); 
     expected.add(3); 
     expected.add(3); 
     assertEquals(expected, paid); 
    } 

    @Test 
    public void whenWeHaveExactAmountButItDoesNotIncludeBiggestCoin_thenPayWithSmallerCoins() throws Exception { 
     // $5, $3, $3, $3 
     List<Integer> inPocket = new ArrayList<>(); 
     inPocket.add(5); 
     inPocket.add(3); 
     inPocket.add(3); 
     inPocket.add(3); 

     Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(6, inPocket); 

     List change = result.get("change"); 
     assertTrue(change.size() == 0); 
     List paid = result.get("paid"); 
     List<Integer> expected = new ArrayList<>(); 
     expected.add(3); 
     expected.add(3); 
     assertEquals(expected, paid); 
    } 
} 

測試是不是乾淨又但它們都經過迄今。我可能會返回並在稍後添加更多測試用例,以查看是否可以打破它,但現在沒有時間。