home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic 4 Unleashed / Visual_Basic_4_Unleashed_SAMS_Publishing_1995.iso / source / chap36 / minimize.bas < prev    next >
BASIC Source File  |  1995-09-19  |  15KB  |  452 lines

  1. Attribute VB_Name = "basMinimize"
  2. '=====================================================================
  3. 'MINIMIZE.BAS by Frank Font 1995
  4. '
  5. 'This VB 4 file contains functions and definitions that carry out a
  6. 'branch and bound minimization algorithm for the MakeTeam program.
  7. '
  8. '*********************************************************************
  9. 'NOTE: These program procedures are for entertainment purposes ONLY.
  10. '=====================================================================
  11. Option Explicit
  12.  
  13. Private AssignmentCost() As Integer  'Set in the init routine.
  14. Private minsTable() As Integer       'Set in the init routine.
  15. Private candidateMap()               'Map candidate records from 0 to max.
  16. Private jobtitleMap()                'Map job records from 0 to max.
  17. '---------------------------------------------------------------------
  18. 'Add all the children of node pointer (np) to the heap.
  19. '---------------------------------------------------------------------
  20. Private Function Expand(np As Long) As Integer
  21.   Dim i As Integer
  22.   ReDim LocalAssignmentCost(EmpsInPool + 1) As Integer  'Array of employees on team.
  23.   Dim se As Long                                        'Pointer to a node.
  24.   
  25.   'Store for later use.
  26.   se = np
  27.  
  28.   'Initialize "all" nodes as available for this job.
  29.   For i = 1 To EmpsInPool
  30.     If gExactSkills And AssignmentCost(memPool(np).job, i - 1) = 0 Then
  31.       'Mark as unavailable.
  32.       LocalAssignmentCost(i) = 0
  33.     Else
  34.       'Mark it as available.
  35.       LocalAssignmentCost(i) = 1
  36.     End If
  37.   Next i
  38.  
  39.   'Travel back the chain to ignore members in further chain building.
  40.   While memPool(se).Parent >= 0
  41.     LocalAssignmentCost(memPool(se).emp) = 0
  42.     se = memPool(se).Parent
  43.   Wend
  44.   
  45.   For i = 1 To EmpsInPool
  46.     If LocalAssignmentCost(i) > 0 Then
  47.       
  48.       'Zero assignment cost indicates no assignment is possible.
  49.       se = Mem_alloc()
  50.       memPool(se).job = memPool(np).job + 1
  51.       memPool(se).emp = i
  52.       memPool(se).Parent = np
  53.       memPool(se).cost = TotalCostGuess(se)
  54.       AddHeap se
  55.       
  56.     End If
  57.     DoEvents
  58.   Next i
  59.  
  60.   Expand = -1  'Okay.
  61. End Function
  62.  
  63.  
  64.  
  65. '---------------------------------------------------------------------
  66. 'Calculate cost of candidate (emp) filling jobtitle (job).
  67. 'This is where the program checks the skills of the emp against
  68. 'the needs of a job.  If the emp has every skill defined by the
  69. 'job, the cost returned is a function of the emp's skill ratings.
  70. 'If one or more required skills is missing the value returned
  71. 'depends as follows:
  72. '  Return 0 if gExactSkills = TRUE because the emp is not exact match.
  73. ' ...OR...
  74. '  Return COST with MAX_Bad factored in if gExactSkills = FALSE
  75. 'A value of ZERO is interpretted elsewhere in the program
  76. 'to mean that the emp should not be considered for
  77. 'the job.
  78. '
  79. 'Ratings are mapped as follows: (11 - data)
  80. 'Input    System
  81. '-----    ------
  82. '1  becomes 10      Worst
  83. '.
  84. '.
  85. '5  becomes 6       Adequate
  86. '.
  87. '.
  88. '10 becomes 1       Best
  89. 'Smaller numbers are better in this implementation of the branch and
  90. 'bound algorithm.  However, 0 is treated as a flag within the program
  91. 'to mean that there should be no consideration given to that emp
  92. 'doing that job.  This is determined by the program when it checks
  93. 'the gExactSkills flag and should never be forced by setting the
  94. 'database value to 11 so that the mapping produces 0.
  95. '
  96. 'Note: Maximum rating in the database is 10 due to inversion method.
  97. '---------------------------------------------------------------------
  98. Private Function CalcCost(job As Integer, emp As Integer) As Integer
  99.   Dim MyTable As Recordset, SQL$
  100.   Dim SkillSet As Recordset
  101.   Dim CostTotal As Integer
  102.   
  103.   CostTotal = 0
  104.   
  105.   'Get the skill set for the given title key.
  106.   SQL$ = "Select SkillKey FROM tblSkillJob WHERE JobtitleKey = " + _
  107.          Str$(jobtitleMap(job))
  108.   Set SkillSet = gMainDB.OpenRecordset(SQL$, dbOpenDynaset)
  109. '  SkillSet.MoveFirst
  110.   Do Until SkillSet.EOF  ' Begin loop.
  111.     
  112.     'Lookup the skill rating for the given skill key.
  113.     SQL$ = "Select Rating from tblCandidateSkill where ((SkillKey=" + _
  114.            Str$(SkillSet!SkillKey) + ")) AND ((CandidateKey=" + _
  115.            Str$(candidateMap(emp)) + "))"
  116.     Set MyTable = gMainDB.OpenRecordset(SQL$, dbOpenDynaset)
  117.     If MyTable.RecordCount = 0 Then
  118.       'This candidate does not have this skill.
  119.       If gExactSkills Then
  120.         'When exact is on, even missing one skill means ineligability.
  121.         MyTable.Close       ' Close table.
  122.         SkillSet.Close
  123.         CalcCost = 0
  124.         Exit Function
  125.       Else
  126.         'Consider for the job but handicap very heavily.
  127.         CostTotal = CostTotal + MAX_Bad
  128.       End If
  129.     Else
  130.       'We have the skill -- invert the rating so we have 1 to 10.
  131.       CostTotal = CostTotal + 11 - MyTable!Rating
  132.     End If
  133.     MyTable.Close       ' Close table.
  134.     SkillSet.MoveNext   ' Locate next record.
  135.   Loop  ' End of loop.
  136.  
  137.   SkillSet.Close
  138.   
  139.   CalcCost = CostTotal
  140.  
  141. End Function
  142.  
  143. '---------------------------------------------------------------------
  144. 'Main branch and bound routine looks for lowest cost team.
  145. 'Returns cost of team.  If no team is found, cost is -1.
  146. '---------------------------------------------------------------------
  147. Public Function BB() As Double
  148.   Dim RootNode As Long
  149.   Dim res As Integer
  150.   Dim tmp$
  151.   
  152.   'Do some initializations and show information.
  153.   MapCandidates
  154.   MapJobtitles
  155.   InitAssignmentTable
  156.   res = InitMinsTable()
  157.   If Not res Then
  158.     Beep
  159.     tmp$ = SQLResultStr(gMainDB, "Name", "tblJobtitle", "JobtitleKey=" + _
  160.            Str$(jobtitleMap(res)))
  161.     Screen.MousePointer = 0
  162.     MsgBox "Cannot continue.  No one is qualified to be '" + _
  163.            tmp$ + "' in this project.  Add or modify candidates" + _
  164.            " or do not require Exact Skill matching.", 48, gProgramTitle
  165.     BB = -1
  166.     Exit Function
  167.   End If
  168.   
  169.   'Create the root node.
  170.   RootNode = Mem_alloc
  171.   memPool(RootNode).emp = 0
  172.   memPool(RootNode).job = 0
  173.   memPool(RootNode).cost = 0
  174.   memPool(RootNode).Parent = -1
  175.  
  176.   While True
  177.     If AnswerNode(RootNode) Then
  178.       BB = PrintSolution(RootNode)
  179.       Exit Function
  180.     Else
  181.       res = Expand(RootNode)
  182.       If EmptyHeap Then
  183.         'There are no more combinations to try.
  184.         Screen.MousePointer = 0
  185.         MsgBox "No teams using the specified criteria and candidate list can be made.", 64, gProgramTitle
  186.         BB = -1
  187.         Exit Function
  188.       Else
  189.         'Get the smallest cost node of the tries so far.
  190.         RootNode = DeleteMin()
  191.       End If
  192.     End If
  193.     DoEvents
  194.     If gCancelMessage > 0 Then
  195.       Beep
  196.       gCancelMessage = -1
  197.       MsgBox "Cancel Detected!", 16, gProgramTitle
  198.       BB = -1
  199.       Exit Function
  200.     End If
  201.   Wend
  202. End Function
  203.  
  204.  
  205. '---------------------------------------------------------------------
  206. 'Pass in node pointer (np) of node to check.  A node is an answer
  207. 'node if its job attribute indicates it is for the last job in the
  208. 'project.
  209. '---------------------------------------------------------------------
  210. Private Function AnswerNode(np As Long) As Boolean
  211.   Dim result As Boolean
  212.   If memPool(np).job = JobsInProject Then
  213.     result = True
  214.   Else
  215.     result = False
  216.   End If
  217.   AnswerNode = result
  218. End Function
  219.  
  220.  
  221.  
  222. '---------------------------------------------------------------------
  223. 'Map the jobtitles records to job numbers of array.
  224. 'This enables mapping of titles to the job assignment matrix.
  225. '---------------------------------------------------------------------
  226. Private Sub MapJobtitles()
  227.   Dim i As Integer
  228.   ReDim jobtitleMap(JobsInProject)
  229.   Dim MyDB As DATABASE, MyTable As Recordset, SQL As String
  230.   
  231.   SQL$ = "Select JobtitleKey FROM tblJobProject WHERE ProjectKey = " + _
  232.          frmMain.cboProjectTeam.BoundText
  233.   
  234.   i = 0
  235.   Set MyTable = gMainDB.OpenRecordset(SQL$, dbOpenDynaset)
  236.   MyTable.MoveFirst
  237.   Do Until MyTable.EOF  ' Begin loop.
  238.     
  239.     jobtitleMap(i) = MyTable!JobtitleKey
  240.     MyTable.MoveNext  ' Locate next record.
  241.     i = i + 1
  242.   
  243.   Loop  ' End of loop.
  244.  
  245.   MyTable.Close ' Close table.
  246. End Sub
  247.  
  248. '---------------------------------------------------------------------
  249. 'Map the candidate primary keys to employee numbers of array for.
  250. '---------------------------------------------------------------------
  251. Private Sub MapCandidates()
  252.  
  253.   Dim i As Integer
  254.   ReDim candidateMap(EmpsInPool)
  255.  
  256.   Dim MyDB As DATABASE, MyTable As Recordset, SQL As String
  257.   SQL$ = "Select CandidateKey FROM tblCandidate WHERE Available = -1"
  258.  
  259.   ' Open recordset.
  260.   i = 0
  261.   Set MyTable = gMainDB.OpenRecordset(SQL$, dbOpenDynaset)
  262.   MyTable.MoveFirst
  263.   Do Until MyTable.EOF  ' Begin loop.
  264.     
  265.     candidateMap(i) = MyTable!CandidateKey
  266.     MyTable.MoveNext  ' Locate next record.
  267.     i = i + 1
  268.   
  269.   Loop  ' End of loop.
  270.  
  271.   MyTable.Close ' Close table.
  272. End Sub
  273.  
  274.  
  275. '---------------------------------------------------------------------
  276. 'Print the solution to the database from node pointed to by np.  Also
  277. 'returns the absolute cost of the solution.
  278. '---------------------------------------------------------------------
  279. Private Function PrintSolution(np As Long) As Double
  280.   Dim i As Integer
  281.   Dim SQL$
  282.   Dim totalcost As Double
  283.   
  284.   totalcost = memPool(np).cost
  285.   While memPool(np).Parent >= 0
  286.     SQL$ = "INSERT INTO tblCandidateProject(" + _
  287.            "CandidateKey, ProjectKey, JobtitleKey) " + _
  288.            "VALUES (" + Str$(candidateMap(memPool(np).emp - 1)) + _
  289.            "," + frmMain.cboProjectTeam.BoundText + _
  290.            "," + Str$(jobtitleMap(memPool(np).job - 1)) + ");"
  291.     gMainDB.Execute SQL, dbFailOnError
  292.     np = memPool(np).Parent
  293.   Wend
  294.   
  295.   'Return the cost of this team.
  296.   PrintSolution = totalcost
  297. End Function
  298.  
  299.  
  300. '---------------------------------------------------------------------
  301. 'A DEBUG ROUTINE
  302. 'Print the Minumum Table contents.
  303. '---------------------------------------------------------------------
  304. Private Sub PrintMinsTable()
  305.   Dim E As Integer
  306.   Dim j As Integer
  307.   Dim line As String
  308.  
  309.   Debug.Print "*** Min table ***"
  310.   For j = 0 To JobsInProject - 1
  311.     line = ""
  312.     For E = 0 To EmpsInPool - 1
  313.       line = line + ", " + Str$(minsTable(j, E))
  314.     Next E
  315.     Debug.Print line + " : " + Str$(minsTable(j, E))
  316.   Next j
  317.  
  318. End Sub
  319.  
  320.  
  321. '---------------------------------------------------------------------
  322. 'A DEBUG ROUTINE
  323. 'Print the Assignment Table contents.
  324. '---------------------------------------------------------------------
  325. Private Sub PrintAssignmentTable()
  326.   Dim E As Integer
  327.   Dim j As Integer
  328.   Dim line As String
  329.  
  330.   Debug.Print "*** Assignment table ***"
  331.   For j = 0 To JobsInProject - 1
  332.     line = ""
  333.     For E = 0 To EmpsInPool - 1
  334.       line = line + ", " + Str$(AssignmentCost(j, E))
  335.     Next E
  336.     Debug.Print line
  337.   Next j
  338.  
  339. End Sub
  340.  
  341. '---------------------------------------------------------------------
  342. 'Initialize the table used to compute the estimated costs.
  343. 'Returns -1 if okay.  On error, returns number of job that triggered it.
  344. 'Note: Assingment cost of 0 means employee cannot do that job so that
  345. '      assignment cost is not considered in the row minimum calculation.
  346. 'Special Note: Sort this table with higher costs first and you will get
  347. '      better performance by inducing smarter pathing early on.
  348. '---------------------------------------------------------------------
  349. Private Function InitMinsTable() As Integer
  350.  
  351.   Dim E As Integer
  352.   Dim j As Integer
  353.   Dim et As Integer
  354.   Dim rowmin As Double
  355.   Dim realmin As Double
  356.  
  357.   ReDim minsTable(JobsInProject, EmpsInPool + 1)
  358.  
  359.   For j = JobsInProject - 1 To 0 Step -1
  360.     realmin = MAX_Double
  361.     For E = 0 To EmpsInPool - 1
  362.       rowmin = MAX_Double
  363.       For et = 0 To EmpsInPool - 1
  364.         'Zero is not considered a cost -- it is a flag.
  365.         If AssignmentCost(j, et) <= rowmin And AssignmentCost(j, et) > 0 Then
  366.           realmin = AssignmentCost(j, et)
  367.           If et <> E Then
  368.             rowmin = realmin
  369.           End If
  370.         End If
  371.       Next et
  372.       'Set minimum value at right of row.
  373.       minsTable(j, E) = rowmin
  374.     Next E
  375.     'Set value at right of row.
  376.     minsTable(j, E) = realmin
  377.     If realmin = MAX_Double Then   'Was the value changed?
  378.       'Fail because no one can do job j.
  379.       InitMinsTable = j
  380.       Exit Function
  381.     End If
  382.   Next j
  383.   
  384.   'Signal that everything is okay so far.
  385.   InitMinsTable = -1
  386. End Function
  387.  
  388.  
  389.  
  390. '---------------------------------------------------------------------
  391. 'Estimate the total cost from this node down.  (Educated guess.)
  392. 'The only requirement for this function is that it return a value
  393. 'that is NEVER higher than the actual cost of going down the path
  394. 'that starts with node np.  (Otherwise the algorithm might ignore  a
  395. 'good combination becuase it looks too expensive.)
  396. '---------------------------------------------------------------------
  397. Private Function TotalCostGuess(np As Long) As Double
  398.   Dim hc As Double
  399.   If memPool(memPool(np).Parent).cost > 0 Then
  400.     hc = ((memPool(memPool(np).Parent).cost) - _
  401.          minsTable(memPool(np).job - 1, EmpsInPool) + _
  402.          AssignmentCost(memPool(np).job - 1, memPool(np).emp - 1))
  403.   Else
  404.     hc = AssignmentCost(memPool(np).job - 1, memPool(np).emp - 1) + _
  405.          FutureCostTerm(np)
  406.   End If
  407.  
  408.   TotalCostGuess = hc
  409. End Function
  410.  
  411.  
  412. '---------------------------------------------------------------------
  413. 'Returns the estimated future cost from this node on.  Cost is sum of
  414. 'row minimums excluding the current column.
  415. '---------------------------------------------------------------------
  416. Private Function FutureCostTerm(np As Long) As Double
  417.   Dim ft As Double
  418.   Dim j As Integer
  419.  
  420.   ft = 0
  421.   For j = memPool(np).job To JobsInProject - 1
  422.     ft = ft + minsTable(j, memPool(np).emp - 1)
  423.   Next j
  424.  
  425.   FutureCostTerm = ft
  426. End Function
  427.  
  428.  
  429. '---------------------------------------------------------------------
  430. 'Initialize the assignement table by filling in cost of an employee
  431. 'for each job in the project.  Higher value means less capable except
  432. 'for zero, which means impossible job for the candidate.
  433. '---------------------------------------------------------------------
  434. Private Sub InitAssignmentTable()
  435.   'Assignment format = AssignmentCost(job, emp)
  436.  
  437.   Dim job As Integer
  438.   Dim emp As Integer
  439.  
  440.   ReDim AssignmentCost(JobsInProject, EmpsInPool)
  441.  
  442.   For job = 0 To JobsInProject
  443.     For emp = 0 To EmpsInPool
  444.       AssignmentCost(job, emp) = CalcCost(job, emp)
  445.     Next emp
  446.   Next job
  447.  
  448. End Sub
  449.  
  450.  
  451.  
  452.