title: Calculating the note mix to dispense
body: <p>I need to figure out a cash mix algorithm to dispense notes from the ATM. The goals of the algorithm are:</p>
  
  <ol>
  <li>To calculate the note mix to dispense the required amount to the customer.</li>
  <li>While doing so, <em>to attempt</em> the emptying of the cash cassettes as evenly as possible, so ideally (but not mandatory), the cash will run out in all cassettes at the same time.</li>
  </ol>
  
  <p>The user wants to get different amounts of cash, such as 500$, 1200$, 6000$, etc.
  There is no average.  The algorithm needs to figure out which notes and how many of them to dispense. Of course, the cassettes in the ATM can change to different values / counts.</p>
  
  <p>Another limitation is that the ATM can present only 30 notes at the time, so the algorithm has to divide the notes in bunches, if calculated number of notes exceeds this limit, while considering the goal above (equal emptying).</p>
  
  <p>Here is what i came up with:</p>
  
  <pre><code>//Represents a typical cash cassette.
  class Cassette
  {
     public int Denom { get; set; } //Denomination: (USD)50, (USD)100, etc.
     public int Count { get; set; } //Number of notes.
  }
  
  //Our cassettes.
  List&lt;Cassette&gt; OriginalCashCassettes = new List&lt;Cassette&gt;();
  List&lt;Cassette&gt; CloneCashCassettes = new List&lt;Cassette&gt;();
  
  //Populated.
  OriginalCashCassettes.Add(new Cassette { Denom = 50, Count = 1000 });
  OriginalCashCassettes.Add(new Cassette { Denom = 100, Count = 1000 });
  OriginalCashCassettes.Add(new Cassette { Denom = 200, Count = 1000 });
  
  //Pass original cassettes to clone cassettes.
  CloneCashCassettes = OriginalCashCassettes;
  
  //Calculate mix for requested amount.
  CalculateNoteMix(6000);
  </code></pre>
  
  <p>And the calculation itself:</p>
  
  <pre><code>private void CalculateNoteMix(int reqAmount)
  {
      //1. Check if the amount is higher than combined counts.
      int totalCounts = 0;
      foreach (var item in CloneCashCassettes)
      {
          totalCounts += item.Denom * item.Count;
      }
  
      if (totalCounts &lt; reqAmount)
      {
          Console.WriteLine("You're trying too high - maximum amount available is: " + totalCounts);
          return;
      }
  
      //2. Check if the amount is dispensable with current denoms.
      int lowestDenom = CloneCashCassettes.Min(c =&gt; c.Denom);
      if (reqAmount % lowestDenom != 0)
      {
          Console.WriteLine("Unable to dispense amount with current denoms");
          return;
      }
  
      //3. Calculate note mix to dispense.
      List&lt;Cassette&gt; noteMix = new List&lt;Cassette&gt;();
  
      do
      {
          //Sort cash cassettes by highest count first.
          CloneCashCassettes = CloneCashCassettes.OrderByDescending(c =&gt; c.Count).ToList();
  
          //Check if highest count denom can cover the amount.
          if (CloneCashCassettes[0].Denom &lt;= reqAmount)
          {
  
              //Check if this denom already exists in the mix.
              Cassette noteMixCassette = noteMix.Find(n =&gt; n.Denom == CloneCashCassettes[0].Denom);
              if (noteMixCassette == null)
              {
                  //Add denom to the note mix.
                  noteMix.Add(new Cassette { Denom = CloneCashCassettes[0].Denom, Count = 1 });
              }
              else
              {
                  //Increase denom count in the note mix.
                  noteMixCassette.Count += 1;
              }
  
              //Reduce denom count in the cash cassette.
              CloneCashCassettes[0].Count -= 1;
              //Reduce the amount by denom.
              reqAmount -= CloneCashCassettes[0].Denom;
          }
          else
          {
              //The amount is smaller than denom =&gt; the denom is unusable - remove it.
              CloneCashCassettes.RemoveAt(0);
          }
  
      //Keep looping until the amount is 0.
      } while (reqAmount &gt; 0);
  
      //Print resulting note mix.
      Console.WriteLine("For the amount of " + reqAmount + ", our note mix is:");
      foreach (var item in noteMix)
      {
          Console.WriteLine("Denom: " + item.Denom + " x " + "Count: " + item.Count + " = " + item.Denom * item.Count);
      }
  }
  </code></pre>
  
  <p>Using this code, if the user asks for $400, then the note mix is:</p>
  
  <pre><code>Denom: 50 x Count: 2 = 100
  Denom: 100 x Count: 1 = 100
  Denom: 200 x Count: 1 = 200
  </code></pre>
  
  <p>Or if the user asks for $25,000, then:</p>
  
  <pre><code>Denom: 50 x Count: 72 = 3600
  Denom: 100 x Count: 72 = 7200
  Denom: 200 x Count: 71 = 14200
  </code></pre>
  
  <p>Problems:</p>
  
  <ol>
  <li><p>This code appears to work fine with denoms of 50 and higher. However, it has a problem with the denom of 20. Any idea how to resolve it?</p>
  
  <p>Example:</p>
  
  <pre><code>    OriginalCashCassettes.Add(new Cassette { Denom = 20, Count = 1000 });
      OriginalCashCassettes.Add(new Cassette { Denom = 50, Count = 1000 });
      OriginalCashCassettes.Add(new Cassette { Denom = 100, Count = 1000 });
  </code></pre>
  
  <p>The user asks for $200, which is dispensable.
  I start to subtract: 200-100-50-20 = 30 - 20 = 10 -> unable to dispense.</p></li>
  <li><p>Same problem with denom of 20 exists in the check if the amount is dispensable (#2 in code).</p>
  
  <p>Example: The cassettes configured as above with denom of 20. User asks for $210, which should be dispensable (100+50+20+20+20).</p></li>
  <li><p>Any ideas how to improve this algorithm in general, so it will be more efficient / faster?</p></li>
  </ol>
  
  <p>Thanks.</p>