Javascript Coin changing / Change making algorithm

前端 未结 3 778
误落风尘
误落风尘 2021-01-25 19:24

So I\'ve been trying to create a program in Javascript/jQuery that splits an amount of money into the smallest amount of dollar bills. So far the program only works with one bil

相关标签:
3条回答
  • 2021-01-25 19:38

    As stated in the comments, it might be a variant of the Knapsack problem.

    Edit : it's actually known as coin changing or change making problem.

    If I understood well, you want a minimal solution to this inequation :

    a1x1 + a2x2 + ... + anxn >= b

    The sum must be as close as possible to b with as few bills as possible.

    Brute force recursive solution

    • Get all possible combination of bills that answer your inequation
    • Find the ones which sum is the closest to the solution
    • Find the solution using the less bills

    //Available bills and money required
    //var availableBills = [2, 5, 8, 16]; var money = 22;
    //var availableBills = [13, 17, 30, 70, 158]; var money = 200;
    var availableBills = [5, 17, 29, 70, 158];
    var money = 200;
    //var availableBills = [5, 10, 178]; var money = 20;
    //var availableBills = [5, 20, 30, 70, 158]; var money = 157;
    //var availableBills = [1, 5, 10]; var money = 97;
    
    //Get all the money in a wallet
    function getWalletSum(wallet) {
      var sum = 0;
      for (var bill in wallet) {
        sum += wallet[bill] * bill;
      }
      return sum;
    }
    
    //Copy a wallet without empty values
    function copyWallet(wallet) {
      var newWallet = {};
      for (var bill in wallet) {
        if (wallet[bill] != 0) {
          newWallet[bill] = wallet[bill];
        }
      }
      return newWallet;
    }
    
    //Merge two wallets without empty values
    function mergeWallets(wallet1, wallet2) {
      var mergedWallet = copyWallet(wallet1);
      for (var bill in wallet2) {
        if (wallet2[bill] != 0) {
          mergedWallet[bill] = wallet2[bill];
        }
      }
      return mergedWallet;
    }
    
    var cycles = 0;
    var loops = 0;
    
    //Get possible wallets
    //Return wallets having sum >= money
    function getPossibleWallets(money, startingBills) {
      cycles++;
      var possibleWallets = [];
      var wallet = {};
      var bills = startingBills.slice();
      var maxBill = bills.pop();
      wallet[maxBill] = Math.ceil(money / maxBill);
      while (wallet[maxBill] >= 0) {
        loops++;
        var walletSum = getWalletSum(wallet);
        if (walletSum == money) {
          possibleWallets.push(copyWallet(wallet));
          return possibleWallets;
        }
        if (walletSum > money) {
          possibleWallets.push(copyWallet(wallet));
        } else {
          if (bills.length > 0) {
            var remaining = money - getWalletSum(wallet);
            var remainingWallets = getPossibleWallets(remaining, bills);
            for (var i = 0; i < remainingWallets.length; i++) {
              var mergedWallet = mergeWallets(wallet, remainingWallets[i]);
              possibleWallets.push(mergedWallet);
              if (getWalletSum(mergedWallet) == money) {
                return possibleWallets;
              }
            };
          }
        }
        wallet[maxBill] = wallet[maxBill] - 1;
      }
      return possibleWallets;
    }
    
    //Get smallest possible wallet
    // > Wallet sum >= money
    // > Wallet sum is as close as possible to money
    // > Wallet is as small as possible (less bills)
    function getSmallestSufficientWallet(money, startingBills) {
      var possibleWallets = getPossibleWallets(money, startingBills);
      console.log(possibleWallets);
      var minWallet = possibleWallets[0];
      for (var i = 0; i < possibleWallets.length; i++) {
        var possibleWallet = possibleWallets[i];
        var possibleWalletSum = getWalletSum(possibleWallet);
        if (possibleWalletSum == money) {
          return possibleWallet;
        }
        if (possibleWalletSum < getWalletSum(minWallet) && possibleWalletSum >= money) {
          minWallet = possibleWallet;
        }
      }
      return minWallet;
    }
    
    var wallet = getSmallestSufficientWallet(money, availableBills);
    console.log('cycles = ' + cycles);
    console.log('loops = ' + loops);
    
    //Array of bills to string
    function billsToString(billsArray) {
      return billsArray.join('$, ') + '$';
    }
    
    //Wallet to string
    function walletToString(wallet) {
      var result = [];
      for (bill in wallet) {
        result.push(wallet[bill] + ' * ' + bill + '$');
      }
      return result.join(' + ');
    }
    
    //Print
    var questionString = '<div>Money : ' + money + '$</div>';
    questionString += '<div>Available : ' + billsToString(availableBills) + '</div>';
    document.getElementById('question').innerHTML = questionString;
    document.getElementById('bills').innerHTML = 'Wallet : ' + walletToString(wallet);
    document.getElementById('sum').innerHTML =
      '<div>Total = ' + getWalletSum(wallet) + '</div>' +
      '<div>Difference = ' + (getWalletSum(wallet) - money) + '</div>';
    <div id="question"></div>
    <div id="bills"></div>
    <div id="sum"></div>

    0 讨论(0)
  • 2021-01-25 19:48

    In a more compacted way:

    function moneyChanger(money, bills){
        if (bills[0] < bills[1]) bills.reverse();
        const change = {};
        bills.map(b => {
           change[b] = Math.floor(money/b);
           money -= b*change[b];
        }) 
        return change
    }
    
    ...
    
    var change = moneyChanger(2995,[5,10,20,50,100,200,500])
    

    The result for this example:

    {5:1, 10:0, 20:2, 50:1, 100:0, 200:2, 500:5}
    
    0 讨论(0)
  • 2021-01-25 19:59
    var bills = [5, 10, 20, 50, 100];
    var money = mod(89);
    
    function mod(num){
        if (num % 5 === 0){
            return num;
        }else{
            return num + 5 - num % 5
        }
    }
    
    function foo(num){
        var index = bills.length - 1;
        var splits = [];
        while (money >= bills[0]){
            if (money >= bills[index]){
               money -= bills[index];
               splits.push(bills[index]);
            }else{
                index--;
            }
        }
        return splits;
    }
    
    console.log(foo(money));
    

    edited jsfiddle

    0 讨论(0)
提交回复
热议问题