Vogel's approximation method

Revision as of 00:27, 4 August 2015 by Rdm (talk | contribs) (Explain tiebreaker on penalty)

Vogel's Approximation Method (VAM) is a technique for finding a good initial feasible solution to an allocation problem.

Task
Vogel's approximation method
You are encouraged to solve this task according to the task description, using any language you may know.

The powers that be have identified 5 tasks that need to be solved urgently. Being imaginative chaps, they have called them “A”, “B”, “C”, “D”, and “E”. They estimate that:

  • A will require 30 hours of work,
  • B will require 20 hours of work,
  • C will require 70 hours of work,
  • D will require 30 hours of work, and
  • E will require 60 hours of work.

They have identified 4 contractors willing to do the work, called “W”, “X”, “Y”, and “Z”.

  • W has 50 hours available to commit to working,
  • X has 60 hours available,
  • Y has 50 hours available, and
  • Z has 50 hours available.

The cost per hour for each contractor for each task is summarized by the following table:

   A  B  C  D  E
W 16 16 13 22 17
X 14 14 13 19 15
Y 19 19 20 23 50
Z 50 12 50 15 11

The task is to use VAM to allocate contractors to tasks. It scales to large problems, so ideally keep sorts out of the iterative cycle. It works as follows:

Step 1: Balance the given transportation problem if either (total supply>total demand) or (total supply<total demand)
Step 2: Determine the penalty cost for each row and column by subtracting the lowest cell cost in the row or column from the next lowest cell cost in the same row or column.
Step 3: Select the row or column with the highest penalty cost (breaking ties arbitrarily or choosing the lowest-cost cell).
Step 4: Allocate as much as possible to the feasible cell with the lowest transportation cost in the row or column with the highest penalty cost.
Step 5: Repeat steps 2, 3 and 4 until all requirements have been meet.
Step 6: Compute total transportation cost for the feasible allocations.

For this task assume that the model is balanced.

For each task and contractor (row and column above) calculating the difference between the smallest two values produces:

        A       B       C       D       E       W       X       Y       Z
1       2       2       0       4       4       3       1       0       1   E-Z(50)

Determine the largest difference (D or E above). In the case of ties I shall choose the one with the lowest price (in this case E because the lowest price for D is Z=15, whereas for E it is Z=11). For your choice determine the minimum cost (chosen E above so Z=11 is chosen now). Allocate as much as possible from Z to E (50 in this case limited by Z's supply). Adjust the supply and demand accordingly. If demand or supply becomes 0 for a given task or contractor it plays no further part. In this case Z is out of it. If you choose arbitrarily, and chose D see here for the working.

Repeat until all supply and demand is met:

2       2       2       0       3       2       3       1       0       -   C-W(50)
3       5       5       7       4      35       -       1       0       -   E-X(10)
4       5       5       7       4       -       -       1       0       -   C-X(20)
5       5       5       -       4       -       -       0       0       -   A-X(30)
6       -      19       -      23       -       -       -       4       -   D-Y(30)
        -       -       -       -       -       -       -       -       -   B-Y(20)

Finally calculate the cost of your solution. In the example given it is £3100:

   A  B  C  D  E
W       50
X 30    20    10
Y    20    30
Z             50

The optimal solution determined by GLPK is £3100:

   A  B  C  D  E
W       50
X 10 20 20    10
Y 20       30
Z             50
Cf.


D

Strongly typed version (but K is not divided in Task and Contractor types to keep code simpler).

Translation of: Python

<lang d>void main() {

   import std.stdio, std.string, std.algorithm, std.range;
   enum K { A, B, C, D, E,  X, Y, Z, W }
   immutable int[K][K] costs = cast() //**
       [K.W: [K.A: 16, K.B: 16, K.C: 13, K.D: 22, K.E: 17],
        K.X: [K.A: 14, K.B: 14, K.C: 13, K.D: 19, K.E: 15],
        K.Y: [K.A: 19, K.B: 19, K.C: 20, K.D: 23, K.E: 50],
        K.Z: [K.A: 50, K.B: 12, K.C: 50, K.D: 15, K.E: 11]];
   int[K] demand, supply;
   with (K)
       demand = [A: 30, B: 20, C: 70, D: 30, E: 60],
       supply = [W: 50, X: 60, Y: 50, Z: 50];
   immutable cols = demand.keys.sort().release;
   auto res = costs.byKey.zip((int[K]).init.repeat).assocArray;
   K[][K] g;
   foreach (immutable x; supply.byKey)
       g[x] = costs[x].keys.schwartzSort!(k => cast()costs[x][k]) //**
              .release;
   foreach (immutable x; demand.byKey)
       g[x] = costs.keys.schwartzSort!(k=> cast()costs[k][x]).release;
   while (g.length) {
       int[K] d, s;
       foreach (immutable x; demand.byKey)
           d[x] = g[x].length > 1 ?
                  costs[g[x][1]][x] - costs[g[x][0]][x] :
                  costs[g[x][0]][x];
       foreach (immutable x; supply.byKey)
           s[x] = g[x].length > 1 ?
                  costs[x][g[x][1]] - costs[x][g[x][0]] :
                  costs[x][g[x][0]];
       auto f = d.keys.minPos!((a,b) => d[a] > d[b])[0];
       auto t = s.keys.minPos!((a,b) => s[a] > s[b])[0];
       if (d[f] > s[t]) {
           t = f;
           f = g[f][0];
       } else {
           f = t;
           t = g[t][0];
       }
       immutable v = min(supply[f], demand[t]);
       res[f][t] += v;
       demand[t] -= v;
       if (demand[t] == 0) {
           foreach (immutable k, immutable n; supply)
               if (n != 0)
                   g[k] = g[k].remove!(c => c == t);
           g.remove(t);
           demand.remove(t);
       }
       supply[f] -= v;
       if (supply[f] == 0) {
           foreach (immutable k, immutable n; demand)
               if (n != 0)
                   g[k] = g[k].remove!(c => c == f);
           g.remove(f);
           supply.remove(f);
       }
   }
   writefln("%-(\t%s%)", cols);
   auto cost = 0;
   foreach (immutable c; costs.keys.sort().release) {
       write(c, '\t');
       foreach (immutable n; cols) {
           if (n in res[c]) {
               immutable y = res[c][n];
               if (y != 0) {
                   y.write;
                   cost += y * costs[c][n];
               }
           }
           '\t'.write;
       }
       writeln;
   }
   writeln("\nTotal Cost = ", cost);

}</lang>

Output:
    A   B   C   D   E
W           50          
X           20      40  
Y   30  20              
Z               30  20  

Total Cost = 3130

J

Implementation:

<lang J>vam=:1 :0

 exceeding=. 0 <. -&(+/)
 D=. x,y exceeding x NB. x: demands
 S=. y,x exceeding y NB. y: sources
 C=. (m,.0),0        NB. m: costs
 B=. 1+>./,C         NB. bigger than biggest cost
 mincost=. <./@-.&0  NB. smallest non-zero cost
 penalty=. |@(B * 2 -/@{. /:~ -. 0:)"1 - mincost"1
 R=. C*0
 while. 0 < +/D,S do.
   pS=. penalty C
   pD=. penalty |:C
   if. pS >&(>./) pD do.
     row=. (i. >./) pS
     col=. (i. mincost) row { C
   else.
     col=. (i. >./) pD
     row=. (i. mincost) col {"1 C
   end.
   n=. (row{S) <. col{D
   S=. (n-~row{S) row} S
   D=. (n-~col{D) col} D
   C=. C * S *&*/ D
   R=. n (<row,col)} R
 end.
 _1 _1 }. R

)</lang>

Note that for our penalty we are using the difference between the two smallest relevant costs multiplied by 1 larger than the highest represented cost and we subtract from that multiple the smallest relevant cost. This gives us the tiebreaker mechanism currently specified for this task.

Task example:

<lang J>demand=: 30 20 70 30 60 src=: 50 60 50 50 cost=: 16 16 13 22 17,14 14 13 19 15,19 19 20 23 50,:50 12 50 15 11

  demand cost vam src
0  0 50  0  0

30 0 20 0 10

0 20  0 30  0
0  0  0  0 50</lang>

Java

Works with: Java version 8

<lang java>import java.util.Arrays; import static java.util.Arrays.stream; import java.util.concurrent.*;

public class VogelsApproximationMethod {

   final static int[] demand = {30, 20, 70, 30, 60};
   final static int[] supply = {50, 60, 50, 50};
   final static int[][] costs = {{16, 16, 13, 22, 17}, {14, 14, 13, 19, 15},
   {19, 19, 20, 23, 50}, {50, 12, 50, 15, 11}};
   final static int nRows = supply.length;
   final static int nCols = demand.length;
   static boolean[] rowDone = new boolean[nRows];
   static boolean[] colDone = new boolean[nCols];
   static int[][] result = new int[nRows][nCols];
   static ExecutorService es = Executors.newFixedThreadPool(2);
   public static void main(String[] args) throws Exception {
       int supplyLeft = stream(supply).sum();
       int totalCost = 0;
       while (supplyLeft > 0) {
           int[] cell = nextCell();
           int r = cell[0];
           int c = cell[1];
           int quantity = Math.min(demand[c], supply[r]);
           demand[c] -= quantity;
           if (demand[c] == 0)
               colDone[c] = true;
           supply[r] -= quantity;
           if (supply[r] == 0)
               rowDone[r] = true;
           result[r][c] = quantity;
           supplyLeft -= quantity;
           totalCost += quantity * costs[r][c];
       }
       stream(result).forEach(a -> System.out.println(Arrays.toString(a)));
       System.out.println("Total cost: " + totalCost);
       es.shutdown();
   }
   static int[] nextCell() throws Exception {
       Future<int[]> f1 = es.submit(() -> maxPenalty(nRows, nCols, true));
       Future<int[]> f2 = es.submit(() -> maxPenalty(nCols, nRows, false));
       int[] res1 = f1.get();
       int[] res2 = f2.get();
       if (res1[3] == res2[3])
           return res1[2] < res2[2] ? res1 : res2;
       return (res1[3] > res2[3]) ? res2 : res1;
   }
   static int[] diff(int j, int len, boolean isRow) {
       int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE;
       int minP = -1;
       for (int i = 0; i < len; i++) {
           if (isRow ? colDone[i] : rowDone[i])
               continue;
           int c = isRow ? costs[j][i] : costs[i][j];
           if (c < min1) {
               min2 = min1;
               min1 = c;
               minP = i;
           } else if (c < min2)
               min2 = c;
       }
       return new int[]{min2 - min1, min1, minP};
   }
   static int[] maxPenalty(int len1, int len2, boolean isRow) {
       int md = Integer.MIN_VALUE;
       int pc = -1, pm = -1, mc = -1;
       for (int i = 0; i < len1; i++) {
           if (isRow ? rowDone[i] : colDone[i])
               continue;
           int[] res = diff(i, len2, isRow);
           if (res[0] > md) {
               md = res[0];  // max diff
               pm = i;       // pos of max diff
               mc = res[1];  // min cost
               pc = res[2];  // pos of min cost
           }
       }
       return isRow ? new int[]{pm, pc, mc, md} : new int[]{pc, pm, mc, md};
   }

}</lang>

[0, 0, 50, 0, 0]
[30, 0, 20, 0, 10]
[0, 20, 0, 30, 0]
[0, 0, 0, 0, 50]
Total cost: 3100

Python

Translation of: Ruby

<lang python>from collections import defaultdict

costs = {'W': {'A': 16, 'B': 16, 'C': 13, 'D': 22, 'E': 17},

         'X': {'A': 14, 'B': 14, 'C': 13, 'D': 19, 'E': 15},
         'Y': {'A': 19, 'B': 19, 'C': 20, 'D': 23, 'E': 50},
         'Z': {'A': 50, 'B': 12, 'C': 50, 'D': 15, 'E': 11}}

demand = {'A': 30, 'B': 20, 'C': 70, 'D': 30, 'E': 60} cols = sorted(demand.iterkeys()) supply = {'W': 50, 'X': 60, 'Y': 50, 'Z': 50} res = dict((k, defaultdict(int)) for k in costs) g = {} for x in supply:

   g[x] = sorted(costs[x].iterkeys(), key=lambda g: costs[x][g])

for x in demand:

   g[x] = sorted(costs.iterkeys(), key=lambda g: costs[g][x])

while g:

   d = {}
   for x in demand:
       d[x] = (costs[g[x][1]][x] - costs[g[x][0]][x]) if len(g[x]) > 1 else costs[g[x][0]][x]
   s = {}
   for x in supply:
       s[x] = (costs[x][g[x][1]] - costs[x][g[x][0]]) if len(g[x]) > 1 else costs[x][g[x][0]]
   f = max(d, key=lambda n: d[n])
   t = max(s, key=lambda n: s[n])
   t, f = (f, g[f][0]) if d[f] > s[t] else (g[t][0], t)
   v = min(supply[f], demand[t])
   res[f][t] += v
   demand[t] -= v
   if demand[t] == 0:
       for k, n in supply.iteritems():
           if n != 0:
               g[k].remove(t)
       del g[t]
       del demand[t]
   supply[f] -= v
   if supply[f] == 0:
       for k, n in demand.iteritems():
           if n != 0:
               g[k].remove(f)
       del g[f]
       del supply[f]

for n in cols:

   print "\t", n,

print cost = 0 for g in sorted(costs):

   print g, "\t",
   for n in cols:
       y = res[g][n]
       if y != 0:
           print y,
       cost += y * costs[g][n]
       print "\t",
   print

print "\n\nTotal Cost = ", cost</lang>

Output:
    A   B   C   D   E
W           50          
X   30      20      10  
Y       20      30      
Z                   50  


Total Cost =  3100

Racket

Losley:

Translation of: Ruby

Strangely, due to the sub-deterministic nature of the hash tables, resources were allocated differently to the #Ruby version; but somehow at the same total cost!

<lang racket>#lang racket (define-values (1st 2nd 3rd) (values first second third))

(define-syntax-rule (?: x t f) (if (zero? x) f t))

(define (hash-ref2

        hsh# key-1 key-2
        #:fail-2 (fail-2 (λ () (error 'hash-ref2 "key-2:~a is not found in hash" key-2)))
        #:fail-1 (fail-1 (λ () (error 'hash-ref2 "key-1:~a is not found in hash" key-1))))
 (hash-ref (hash-ref hsh# key-1 fail-1) key-2 fail-2))

(define (VAM costs all-supply all-demand)

 (define (reduce-g/x g/x x#-- x x-v y y-v)
   (for/fold ((rv (?: x-v g/x (hash-remove g/x x))))
     (#:when (zero? y-v) ((k n) (in-hash x#--)) #:unless (zero? n))
     (hash-update rv k (curry remove y))))
 
 (define (cheapest-candidate/tie-break candidates)
   (define cand-max3 (3rd (argmax 3rd candidates)))
   (argmin 2nd (for/list ((cand candidates) #:when (= (3rd cand) cand-max3)) cand)))
 
 (let vam-loop
   ((res (hash))
    (supply all-supply)
    (g/supply
     (for/hash ((x (in-hash-keys all-supply)))
       (define costs#x (hash-ref costs x))
       (define key-fn (λ (g) (hash-ref costs#x g)))
       (values x (sort (hash-keys costs#x) < #:key key-fn #:cache-keys? #t))))
    (demand all-demand)
    (g/demand
     (for/hash ((x (in-hash-keys all-demand)))
       (define key-fn (λ (g) (hash-ref2 costs g x)))
       (values x (sort (hash-keys costs) < #:key key-fn #:cache-keys? #t)))))
   (cond
     [(and (hash-empty? supply) (hash-empty? demand)) res]
     [(or (hash-empty? supply) (hash-empty? demand)) (error 'VAM "Unbalanced supply / demand")]
     [else
      (define D
        (let ((candidates
               (for/list ((x (in-hash-keys demand)))
                 (match-define (hash-table ((== x) (and g#x (list g#x.0 _ ...))) _ ...) g/demand)
                 (define z (hash-ref2 costs g#x.0 x))
                 (match g#x
                   [(list _ g#x.1 _ ...) (list x z (- (hash-ref2 costs g#x.1 x) z))]
                   [(list _) (list x z z)]))))
          (cheapest-candidate/tie-break candidates)))
      
      (define S
        (let ((candidates
               (for/list ((x (in-hash-keys supply)))
                 (match-define (hash-table ((== x) (and g#x (list g#x.0 _ ...))) _ ...) g/supply)
                 (define z (hash-ref2 costs x g#x.0))
                 (match g#x
                   [(list _ g#x.1 _ ...) (list x z (- (hash-ref2 costs x g#x.1) z))]
                   [(list _) (list x z z)]))))
          (cheapest-candidate/tie-break candidates)))
      
      (define-values (d s)
        (let ((t>f? (if (= (3rd D) (3rd S)) (> (2nd S) (2nd D)) (> (3rd D) (3rd S)))))
          (if t>f? (values (1st D) (1st (hash-ref g/demand (1st D))))
              (values (1st (hash-ref g/supply (1st S))) (1st S)))))
      
      (define v (min (hash-ref supply s) (hash-ref demand d)))
      
      (define d-v (- (hash-ref demand d) v))
      (define s-v (- (hash-ref supply s) v))
      
      (define demand-- (?: d-v (hash-set demand d d-v) (hash-remove demand d)))
      (define supply-- (?: s-v (hash-set supply s s-v) (hash-remove supply s)))
      
      (vam-loop
       (hash-update res s (λ (h) (hash-update h d (λ (x) (+ v x)) 0)) hash)
       supply-- (reduce-g/x g/supply supply-- s s-v d d-v)
       demand-- (reduce-g/x g/demand demand-- d d-v s s-v))])))

(define (vam-solution-cost costs demand?cols solution)

 (match demand?cols
   [(? list? demand-cols)
    (for*/sum ((g (in-hash-keys costs)) (n (in-list demand-cols)))
      (* (hash-ref2 solution g n #:fail-2 0) (hash-ref2 costs g n)))]
   [(hash-table (ks _) ...) (vam-solution-cost costs (sort ks symbol<? solution))]))

(define (describe-VAM-solution costs demand sltn)

 (define demand-cols (sort (hash-keys demand) symbol<?))
 (string-join
  (map
   (curryr string-join "\t")
   `(,(map ~a (cons "" demand-cols))
     ,@(for/list ((g (in-hash-keys costs)))
         (cons (~a g) (for/list ((c demand-cols)) (~a (hash-ref2 sltn g c #:fail-2 "-")))))
     ()
     ("Total Cost:" ,(~a (vam-solution-cost costs demand-cols sltn)))))
  "\n"))
--------------------------------------------------------------------------------------------------

(let ((COSTS (hash 'W (hash 'A 16 'B 16 'C 13 'D 22 'E 17)

                  'X (hash 'A 14 'B 14 'C 13 'D 19 'E 15)
                  'Y (hash 'A 19 'B 19 'C 20 'D 23 'E 50)
                  'Z (hash 'A 50 'B 12 'C 50 'D 15 'E 11)))      
     (DEMAND (hash 'A 30 'B 20 'C 70 'D 30 'E 60))
     (SUPPLY (hash 'W 50 'X 60 'Y 50 'Z 50)))  
 (displayln (describe-VAM-solution COSTS DEMAND (VAM COSTS SUPPLY DEMAND))))</lang>
Output:
	A	B	C	D	E
W	-	-	50	-	-
X	10	20	20	-	10
Y	20	-	-	30	-
Z	-	-	-	-	50

Total Cost:	3100

Ruby

Breaks ties using lowest cost cell.

Task Example

<lang ruby># VAM

  1. Nigel_Galloway
  2. September 1st., 2013

COSTS = {W: {A: 16, B: 16, C: 13, D: 22, E: 17},

         X: {A: 14, B: 14, C: 13, D: 19, E: 15},
         Y: {A: 19, B: 19, C: 20, D: 23, E: 50},
         Z: {A: 50, B: 12, C: 50, D: 15, E: 11}}

demand = {A: 30, B: 20, C: 70, D: 30, E: 60} supply = {W: 50, X: 60, Y: 50, Z: 50} COLS = demand.keys res = {}; COSTS.each_key{|k| res[k] = Hash.new(0)} g = {}; supply.each_key{|x| g[x] = COSTS[x].keys.sort_by{|g| COSTS[x][g]}}

       demand.each_key{|x| g[x] = COSTS.keys.sort_by{|g| COSTS[g][x]}}

until g.empty?

 d = demand.collect{|x,y| [x, z = COSTS[g[x][0]][x], g[x][1] ? COSTS[g[x][1]][x] - z : z]}
 dmax = d.max_by{|n| n[2]}
 d = d.select{|x| x[2] == dmax[2]}.min_by{|n| n[1]}
 s = supply.collect{|x,y| [x, z = COSTS[x][g[x][0]], g[x][1] ? COSTS[x][g[x][1]] - z : z]}
 dmax = s.max_by{|n| n[2]}
 s = s.select{|x| x[2] == dmax[2]}.min_by{|n| n[1]}
 t,f = d[2]==s[2] ? [s[1], d[1]] : [d[2],s[2]] 
 d,s = t > f ? [d[0],g[d[0]][0]] : [g[s[0]][0],s[0]]
 v = [supply[s], demand[d]].min
 res[s][d] += v
 demand[d] -= v
 if demand[d] == 0 then
   supply.reject{|k, n| n == 0}.each_key{|x| g[x].delete(d)}
   g.delete(d)
   demand.delete(d)
 end
 supply[s] -= v
 if supply[s] == 0 then
   demand.reject{|k, n| n == 0}.each_key{|x| g[x].delete(s)}
   g.delete(s)
   supply.delete(s)
 end

end

COLS.each{|n| print "\t", n} puts cost = 0 COSTS.each_key do |g|

 print g, "\t"
 COLS.each do |n|
   y = res[g][n]
   print y if y != 0
   cost += y * COSTS[g][n]
   print "\t"
 end
 puts

end print "\n\nTotal Cost = ", cost</lang>

Output:
        A       B       C       D       E
W                       50
X       30              20              10
Y               20              30
Z                                       50


Total Cost = 3100

Reference Example

Replacing the data in the Task Example with: <lang ruby>COSTS = {S1: {D1: 46, D2: 74, D3: 9, D4: 28, D5: 99},

         S2: {D1: 12, D2:  75, D3:  6, D4: 36, D5: 48},
         S3: {D1: 35, D2: 199, D3:  4, D4:  5, D5: 71},
         S4: {D1: 61, D2:  81, D3: 44, D4: 88, D5:  9},
         S5: {D1: 85, D2:  60, D3: 14, D4: 25, D5: 79}}

demand = {D1: 278, D2: 60, D3: 461, D4: 116, D5: 1060} supply = {S1: 461, S2: 277, S3: 356, S4: 488, S5: 393}</lang> Produces:

        D1      D2      D3      D4      D5
S1      1       60      68              332
S2      277
S3                              116     240
S4                                      488
S5                      393


Total Cost = 68804

Tcl

Works with: Tcl version 8.6

<lang tcl>package require Tcl 8.6

  1. A sort that works by sorting by an auxiliary key computed by a lambda term

proc sortByFunction {list lambda} {

   lmap k [lsort -index 1 [lmap k $list {

list $k [uplevel 1 [list apply $lambda $k]]

   }]] {lindex $k 0}

}

  1. A simple way to pick a “best” item from a list

proc minimax {list maxidx minidx} {

   set max -Inf; set min Inf
   foreach t $list {

if {[set m [lindex $t $maxidx]] > $max} { set best $t set max $m set min Inf } elseif {$m == $max && [set m [lindex $t $minidx]] < $min} { set best $t set min $m }

   }
   return $best

}

  1. The approximation engine. Note that this does not change the provided
  2. arguments at all since they are copied on write.

proc VAM {costs demand supply} {

   # Initialise the sorted sequence of pairs and the result dictionary
   foreach x [dict keys $demand] {

dict set g $x [sortByFunction [dict keys $supply] {g { upvar 1 costs costs x x; dict get $costs $g $x }}] dict set row $x 0

   }
   foreach x [dict keys $supply] {

dict set g $x [sortByFunction [dict keys $demand] {g { upvar 1 costs costs x x; dict get $costs $x $g }}] dict set res $x $row

   }
   # While there's work to do...
   while {[dict size $g]} {

# Select "best" demand lassign [minimax [lmap x [dict keys $demand] { if {![llength [set gx [dict get $g $x]]]} continue set z [dict get $costs [lindex $gx 0] $x] if {[llength $gx] > 1} { list $x $z [expr {[dict get $costs [lindex $gx 1] $x] - $z}] } else { list $x $z $z } }] 2 1] d dVal dCost

# Select "best" supply lassign [minimax [lmap x [dict keys $supply] { if {![llength [set gx [dict get $g $x]]]} continue set z [dict get $costs $x [lindex $gx 0]] if {[llength $gx] > 1} { list $x $z [expr {[dict get $costs $x [lindex $gx 1]] - $z}] } else { list $x $z $z } }] 2 1] s sVal sCost

# Compute how much to transfer, and with which "best" if {$sCost == $dCost ? $sVal > $dVal : $sCost < $dCost} { set s [lindex [dict get $g $d] 0] } else { set d [lindex [dict get $g $s] 0] } set v [expr {min([dict get $supply $s], [dict get $demand $d])}]

# Transfer some supply to demand dict update res $s inner {dict incr inner $d $v} dict incr demand $d -$v if {[dict get $demand $d] == 0} { dict for {k n} $supply { if {$n != 0} { # Filter list in dictionary to remove element dict set g $k [lmap x [dict get $g $k] { if {$x eq $d} continue; set x }] } } dict unset g $d dict unset demand $d } dict incr supply $s -$v if {[dict get $supply $s] == 0} { dict for {k n} $demand { if {$n != 0} { dict set g $k [lmap x [dict get $g $k] { if {$x eq $s} continue; set x }] } } dict unset g $s dict unset supply $s }

   }
   return $res

}</lang> Demonstration: <lang tcl>set COSTS {

   W {A 16 B 16 C 13 D 22 E 17}
   X {A 14 B 14 C 13 D 19 E 15}
   Y {A 19 B 19 C 20 D 23 E 50}
   Z {A 50 B 12 C 50 D 15 E 11}

} set DEMAND {A 30 B 20 C 70 D 30 E 60} set SUPPLY {W 50 X 60 Y 50 Z 50}

set RES [VAM $COSTS $DEMAND $SUPPLY]

puts \t[join [dict keys $DEMAND] \t] set cost 0 foreach g [dict keys $SUPPLY] {

   puts $g\t[join [lmap n [dict keys $DEMAND] {

set c [dict get $RES $g $n] incr cost [expr {$c * [dict get $COSTS $g $n]}] expr {$c ? $c : ""}

   }] \t]

} puts "\nTotal Cost = $cost"</lang>

Output:
        A       B       C       D       E
W                       50              
X       10      20      20              10
Y       20                      30      
Z                                       50

Total Cost = 3100

zkl

Translation of: Python
Translation of: Ruby

<lang zkl>costs :=D("W",D("A",16, "B",16, "C",13, "D",22, "E",17),

         "X",D("A",14, "B",14, "C",13, "D",19, "E",15),
         "Y",D("A",19, "B",19, "C",20, "D",23, "E",50),
         "Z",D("A",50, "B",12, "C",50, "D",15, "E",11)).makeReadOnly();

demand:=D("A",30, "B",20, "C",70, "D",30, "E",60); // gonna be modified supply:=D("W",50, "X",60, "Y",50, "Z",50); // gonna be modified</lang> <lang zkl>cols  :=demand.keys.sort(); res  :=vogel(costs,supply,demand); cost  :=0; println("\t",cols.concat("\t")); foreach g in (costs.keys.sort()){

  print(g,"\t");
  foreach n in (cols){
     y:=res[g].find(n);
     if(y){ y=y[0]; print(y); cost += y*costs[g][n]; }
     print("\t");
  }
  println();

} println("\nTotal Cost = ",cost);</lang> <lang zkl>fcn vogel(costs,supply,demand){

  // a Dictionary can be created via a list of (k,v) pairs
  res := D(costs.pump(List,fcn([(k,_)]){ return(k,D()) }));
  g   := D(); // cross index costs and make writable
  supply.pump(Void,'wrap([(k,_)]){ g[k] = 
     costs[k].keys.sort('wrap(a,b){ costs[k][a]<costs[k][b] }).copy() });
  demand.pump(Void,'wrap([(k,_)]){ g[k] = 
     costs.keys.sort('wrap(a,b){ costs[a][k]<costs[b][k] }).copy() });
  while(g){
     d:=D(demand.pump(List,'wrap([(k,_)]){ return(k,

g[k][0,2].apply('wrap(gk){ costs[gk][k] }).reverse().reduce('-)) }));

     s:=D(supply.pump(List,'wrap([(k,_)]){ return(k,

g[k][0,2].apply('wrap(gk){ costs[k][gk] }).reverse().reduce('-)) }));

     f:=(0).max(d.values); f=d.filter('wrap([(_,v)]){ v==f })[-1][0];
     t:=(0).max(s.values); t=s.filter('wrap([(_,v)]){ v==t })[-1][0];
     t,f=(if(d[f]>s[t]) T(f,g[f][0]) else T(g[t][0],t));
     v:=supply[f].min(demand[t]);
     res[f].appendV(t,v);  // create t:(v) or append v to t:(...)
     if(0 == (demand[t] -= v)){

supply.pump(Void,'wrap([(k,n)]){ if(n!=0) g[k].remove(t) }); g.del(t); demand.del(t);

     }
     if(0 == (supply[f] -= v)){

demand.pump(Void,'wrap([(k,n)]){ if(n!=0) g[k].remove(f) }); g.del(f); supply.del(f);

     }
  }//while
  res

}</lang>

Output:
	A	B	C	D	E
W			50			
X	10	20	20		10	
Y	20			30		
Z					50	

Total Cost = 3100