Highest Permutation Value

tazeo

Board Regular
Joined
Feb 15, 2007
Messages
133
Office Version
  1. 365
Platform
  1. Windows
I am think what I want to do is in the realms of the GURU excel user :) and well beyond what I can do.

This is for an online sports management game I play. Basically I have calculated each players skills in each position, and I need to find the best combination of the players on the field to give me the best team.

I have a table as below, and I need to find a solution that gives me what the best combination will be.
OTB1.xls
ABCDEFGHIJKLMNOPQRS
1NamePos1Pos2Pos3Pos4Pos5Pos5aPos6Pos7Pos8Pos8aPos9Pos9aPos10Pos11Pos12Pos12aPos13Pos13a
2Player149.20039.65044.80146.89056.33056.33063.90046.73554.80054.80049.17049.17056.48057.37064.00064.00052.50052.500
3Player250.06045.05054.67346.83055.74055.74069.13066.03560.10060.10066.26066.26054.72058.25057.40057.40054.40054.400
4Player348.13051.90060.35448.72048.87048.87056.90063.85550.20050.20066.81066.81049.48054.32048.30048.30058.40058.400
5Player446.52042.70049.30337.21038.32038.32044.72049.19541.10041.10051.71051.71035.84038.59037.20037.20042.40042.400
6Player559.96047.60056.18755.13067.97067.97077.65052.97060.80060.80064.84064.84057.02064.05065.30065.30064.50064.500
7Player646.07040.65039.95535.13028.63028.63036.40027.03525.90025.90037.64037.64024.04022.76027.60027.60041.60041.600
8Player763.13060.95065.75062.70060.06060.06062.72046.46551.80051.80059.83059.83045.73049.51053.60053.60058.00058.000
9Player859.17059.75067.93062.06064.18064.18070.21064.30561.30061.30070.22070.22059.84064.45063.90063.90065.50065.500
10Player947.46040.35051.76241.64049.89049.89058.90045.70046.50046.50058.32058.32022.98032.82029.00029.00033.30033.300
11Player1055.08055.10054.35057.00049.51049.51052.17040.09046.50046.50044.63044.63050.26045.84057.60057.60052.90052.900
12Player1169.71055.35058.53961.33067.57067.57080.05050.99562.70062.70061.69061.69062.04061.81072.80072.80069.00069.000
13Player1246.94042.10043.04143.05041.54041.54052.75038.88040.80040.80045.20045.20040.19038.07045.20045.20048.10048.100
14Player1359.03053.85063.98159.37065.96065.96071.71054.85561.60061.60064.45064.45044.88051.56054.00054.00047.30047.300
15Player1452.17049.60055.12050.68050.28050.28054.75045.40548.60048.60051.54051.54038.65040.51045.40045.40041.80041.800
16Player1561.45060.25065.97358.77054.88054.88062.36055.78555.90055.90061.50061.50042.29042.62049.10049.10046.80046.800
17Player1668.19063.20072.48262.18062.73062.73071.57061.13062.70062.70070.32070.32040.37044.42048.90048.90046.70046.700
18Player1751.36035.65043.34537.67051.00051.00069.55055.29555.40055.40058.56058.56047.59049.68052.90052.90049.90049.900
19Player1839.62031.30035.68631.54035.47035.47039.63028.80533.00033.00035.69035.69025.79028.57031.40031.40031.00031.000
20Player1963.40054.95059.91753.23053.88053.88064.98057.44559.10059.10058.81058.81048.40045.88056.00056.00047.60047.600
21Player2058.67053.55060.94057.18059.35059.35064.38046.61554.80054.80057.31057.31037.56041.89047.30047.30042.20042.200
Sheet2


Can anyone help?
 
Hi

How about this revised code - same deal as before, but it gives a higher total team score.

Code:
Public Sub FindAnotherCombo()

On Error GoTo EH

Const InputSheet As String = "Sheet1" 'change this to your input sheet name
Const OutputSheet As String = "Sheet3" 'change this to your output sheet name
Const StartRow As Long = "1" 'set this to the first row number (including the headings)
Const StartCol As Long = "1" 'set this to the first column number, e.g. "A" = 1, "B" = 2

Dim NumPlayers As Long, _
    NumPositions As Long, _
    BestPosition As Long, _
    BestPlayer As Long, _
    CurrentTotal As Double, _
    NewTotal As Double, _
    tempVar As Long
Dim ScenarioLoop As Long, _
    PositionLoop As Long, _
    PlayerLoop As Long
Dim PlayerArray() As String, _
    PositionsArray() As String, _
    ScoreArray() As Double, _
    ResultsArray() As Long

NumPlayers = Sheets(InputSheet).Cells(StartRow, StartCol).End(xlDown).Row - 1
NumPositions = Sheets(InputSheet).Cells(StartRow, StartCol).End(xlToRight).Column - 1

'Initialise the arrays
ReDim PlayerArray(NumPlayers, 1) 'holds the players names & used indicator
ReDim PositionsArray(NumPositions, 1) 'holds the position names and a used indicator
ReDim ResultsArray(NumPositions, 1) 'holds the best position and player numbers
ReDim ScoreArray(NumPlayers, NumPositions) 'hold the players scores

'Import the names, positions & scores
With Sheets(InputSheet)
    For PlayerLoop = 1 To NumPlayers
        PlayerArray(PlayerLoop, 0) = _
            .Cells(StartRow + PlayerLoop, StartCol).Value
        PlayerArray(PlayerLoop, 1) = 0 'not used indicator
        For PositionLoop = 1 To NumPositions
            ScoreArray(PlayerLoop, PositionLoop) = _
                .Cells(StartRow + PlayerLoop, StartCol + PositionLoop).Value
        Next PositionLoop
    Next PlayerLoop
    For PositionLoop = 1 To NumPositions
        PositionsArray(PositionLoop, 0) = _
            .Cells(StartRow, StartCol + PositionLoop).Value
        PositionsArray(PositionLoop, 1) = 0 'clear the used field for the positions
        ResultsArray(PositionLoop, 0) = 0 'clear the results array
        ResultsArray(PositionLoop, 1) = 0
    Next PositionLoop
End With

'Pick the highest values first
ScoreArray(0, 0) = 0
For ScenarioLoop = 1 To NumPositions
    BestPosition = 0
    BestPlayer = 0
    For PositionLoop = 1 To NumPositions
        If PositionsArray(PositionLoop, 1) = 0 Then
            For PlayerLoop = 1 To NumPlayers
                If PlayerArray(PlayerLoop, 1) = 0 Then
                    If ScoreArray(PlayerLoop, PositionLoop) > _
                        ScoreArray(BestPlayer, BestPosition) Then
                            PlayerArray(BestPlayer, 1) = 0
                            PositionsArray(BestPosition, 1) = 0
                            BestPlayer = PlayerLoop
                            BestPosition = PositionLoop
                            PlayerArray(PlayerLoop, 1) = 1
                            PositionsArray(PositionLoop, 1) = 1
                    End If
                End If
            Next PlayerLoop
        End If
    Next PositionLoop
    ResultsArray(ScenarioLoop, 0) = BestPlayer
    ResultsArray(ScenarioLoop, 1) = BestPosition
Next ScenarioLoop

'Compare each pair to see if there is a swap that will result in a higher score
tempVar = 1
Do While tempVar <> 0
    tempVar = 0
    For ScenarioLoop = 1 To NumPositions
        For PlayerLoop = 1 To NumPositions
            CurrentTotal = ScoreArray(ResultsArray(ScenarioLoop, 0), ResultsArray(ScenarioLoop, 1)) _
                + ScoreArray(ResultsArray(PlayerLoop, 0), ResultsArray(PlayerLoop, 1))
            NewTotal = ScoreArray(ResultsArray(ScenarioLoop, 0), ResultsArray(PlayerLoop, 1)) _
                + ScoreArray(ResultsArray(PlayerLoop, 0), ResultsArray(ScenarioLoop, 1))
            If NewTotal > CurrentTotal Then
                tempVar = tempVar + 1
                BestPosition = ResultsArray(ScenarioLoop, 1)
                ResultsArray(ScenarioLoop, 1) = ResultsArray(PlayerLoop, 1)
                ResultsArray(PlayerLoop, 1) = BestPosition
            End If
        Next PlayerLoop
    Next ScenarioLoop
Loop

'Show the results
With Sheets(OutputSheet)
    For ScenarioLoop = 1 To NumPositions
        .Cells(ScenarioLoop, 1) = PositionsArray(ResultsArray(ScenarioLoop, 1), 0)
        .Cells(ScenarioLoop, 2) = PlayerArray(ResultsArray(ScenarioLoop, 0), 0)
        .Cells(ScenarioLoop, 3) = ScoreArray(ResultsArray(ScenarioLoop, 0), ResultsArray(ScenarioLoop, 1))
    Next ScenarioLoop
End With

MsgBox "Finished!", vbInformation, "Done..."

Exit Sub

EH:
    MsgBox Err.Description, vbCritical, "Error# " & Err.Number

End Sub

Andrew
 
Upvote 0

Excel Facts

Easy bullets in Excel
If you have a numeric keypad, press Alt+7 on numeric keypad to type a bullet in Excel.
Or try this technique:

Instead of picking out the highest values and then trying to find higher value pairs (which is what the previous version does), this starts with the position that has the lowest possible high score, populates that first and repeats this process for the second to lowest possible high score and so on. Then it sees if it can swap pairs to come up with a better result.

It's not too bad considering we aren't testing all of the possible permutations......

Andrew

Code:
Public Sub FindComboNew()

On Error GoTo EH

Const InputSheet As String = "Sheet1" 'change this to your input sheet name
Const OutputSheet As String = "Sheet4" 'change this to your output sheet name
Const StartRow As Long = "1" 'set this to the first row number (including the headings)
Const StartCol As Long = "1" 'set this to the first column number, e.g. "A" = 1, "B" = 2

Dim NumPlayers As Long, _
    NumPositions As Long, _
    BestPosition As Long, _
    BestPlayer As Long, _
    tempVar As Long, _
    CurrentTotal As Double, _
    NewTotal As Double
Dim ScenarioLoop As Long, _
    PositionLoop As Long, _
    PlayerLoop As Long
Dim PlayerArray() As String, _
    PositionsArray() As String, _
    ScoreArray() As Double, _
    ResultsArray() As Long

NumPlayers = Sheets(InputSheet).Cells(StartRow, StartCol).End(xlDown).Row - 1
NumPositions = Sheets(InputSheet).Cells(StartRow, StartCol).End(xlToRight).Column - 1

'Initialise the arrays
ReDim PlayerArray(NumPlayers, 1) 'holds the players names and a used indicator
ReDim PositionsArray(NumPositions, 3) 'holds the position names, used indicator, hi-score and ranking
ReDim ResultsArray(NumPositions, 1) 'holds the best position and player numbers
ReDim ScoreArray(NumPlayers, NumPositions) 'hold the players scores

'Import the names, positions & scores
With Sheets(InputSheet)
    For PositionLoop = 1 To NumPositions
        PositionsArray(PositionLoop, 0) = .Cells(StartRow, StartCol + PositionLoop).Value
        PositionsArray(PositionLoop, 1) = 0 'clear the used field for the positions
        PositionsArray(PositionLoop, 2) = 0 'clear the ranking field
        PositionsArray(PositionLoop, 3) = 0 'clear the hi-score field
        ResultsArray(PositionLoop, 0) = 0 'clear the results array
        ResultsArray(PositionLoop, 1) = 0
    Next PositionLoop
    For PlayerLoop = 1 To NumPlayers
        PlayerArray(PlayerLoop, 0) = .Cells(StartRow + PlayerLoop, StartCol).Value
        PlayerArray(PlayerLoop, 1) = 0 'used indicator
        For PositionLoop = 1 To NumPositions
            ScoreArray(PlayerLoop, PositionLoop) = _
            .Cells(StartRow + PlayerLoop, StartCol + PositionLoop).Value
                If ScoreArray(PlayerLoop, PositionLoop) > _
                PositionsArray(PositionLoop, 3) Then
                    PositionsArray(PositionLoop, 3) = ScoreArray(PlayerLoop, PositionLoop)
            End If
        Next PositionLoop
    Next PlayerLoop
End With

For ScenarioLoop = 1 To NumPositions
    For PositionLoop = 1 To NumPositions
        If PositionsArray(PositionLoop, 3) > PositionsArray(ScenarioLoop, 3) Then
           PositionsArray(ScenarioLoop, 2) = PositionsArray(ScenarioLoop, 2) + 1
        ElseIf PositionsArray(PositionLoop, 3) = PositionsArray(ScenarioLoop, 3) Then
            If PositionLoop > ScenarioLoop Then
               PositionsArray(ScenarioLoop, 2) = PositionsArray(ScenarioLoop, 2) + 1
            End If
        End If
    Next PositionLoop
    Debug.Print PositionsArray(ScenarioLoop, 2) & " : " & PositionsArray(ScenarioLoop, 3)
Next ScenarioLoop

ScoreArray(0, 0) = 0
For ScenarioLoop = NumPositions To 1 Step -1
    BestPosition = 0
    BestPlayer = 0
    For PositionLoop = 1 To NumPositions
        If PositionsArray(PositionLoop, 2) = ScenarioLoop - 1 Then
            For PlayerLoop = 1 To NumPlayers
                If PlayerArray(PlayerLoop, 1) = 0 Then
                    If ScoreArray(PlayerLoop, PositionLoop) > _
                    ScoreArray(BestPlayer, BestPosition) Then
                        PlayerArray(BestPlayer, 1) = 0
                        BestPlayer = PlayerLoop
                        BestPosition = PositionLoop
                        PlayerArray(PlayerLoop, 1) = 1
                    End If
                End If
            Next PlayerLoop
        End If
    Next PositionLoop
    ResultsArray(ScenarioLoop, 0) = BestPlayer
    ResultsArray(ScenarioLoop, 1) = BestPosition
Next ScenarioLoop

'Compare each pair to see if there is a swap that will result in a higher score
tempVar = 1
Do While tempVar <> 0
    tempVar = 0
    For ScenarioLoop = 1 To NumPositions
        For PlayerLoop = 1 To NumPositions
            CurrentTotal = ScoreArray(ResultsArray(ScenarioLoop, 0), _
                                ResultsArray(ScenarioLoop, 1)) _
                            + ScoreArray(ResultsArray(PlayerLoop, 0), _
                                ResultsArray(PlayerLoop, 1))
            NewTotal = ScoreArray(ResultsArray(ScenarioLoop, 0), _
                            ResultsArray(PlayerLoop, 1)) _
                        + ScoreArray(ResultsArray(PlayerLoop, 0), _
                            ResultsArray(ScenarioLoop, 1))
            If NewTotal > CurrentTotal Then
                tempVar = tempVar + 1
                BestPosition = ResultsArray(ScenarioLoop, 1)
                ResultsArray(ScenarioLoop, 1) = ResultsArray(PlayerLoop, 1)
                ResultsArray(PlayerLoop, 1) = BestPosition
            End If
        Next PlayerLoop
    Next ScenarioLoop
    Debug.Print "total swaps = " & tempVar
Loop

'Show the results
With Sheets(OutputSheet)
    For ScenarioLoop = 1 To NumPositions
        .Cells(ScenarioLoop, 1) = PositionsArray(ResultsArray(ScenarioLoop, 1), 0)
        .Cells(ScenarioLoop, 2) = PlayerArray(ResultsArray(ScenarioLoop, 0), 0)
        .Cells(ScenarioLoop, 3) = ScoreArray(ResultsArray(ScenarioLoop, 0), ResultsArray(ScenarioLoop, 1))
    Next ScenarioLoop
End With

MsgBox "Processing complete...", vbInformation, "Finished!"

Exit Sub

EH:
    MsgBox Err.Description, vbCritical, "Error# " & Err.Number

End Sub
 
Upvote 0
Andrew, you did also have some sparetime, isn't it? :D

tazeo,
It would be great to check the permutation count. When it's too high - you decide what number - the code would take Andrews system which is "very close", else a "correct" system like mine.
 
Upvote 0
I'll chuck in another approach. This one does most of the heavy lifting with some worksheet formulas, and leaves the working on the sheet. (It can be removed if you want).

Code...
Code:
Sub FindBestTeam()
Dim Rw As Long, _
    Col As Long, _
    i As Long, _
    j As Long
Dim rngRef As Range, _
    rngWhichCol As Range, _
    rngMaxVal As Range, _
    rngWhichPlayer As Range, _
    rngOutput As Range
Const LOW_RANK = 1000

Application.Calculation = xlCalculationManual
'write MAX formula to bottom of table
Rw = Range("A1").CurrentRegion.Rows.Count
Col = Range("A1").CurrentRegion.Columns.Count
Cells(Rw + 1, 2).Resize(1, Col - 1).FormulaR1C1 = _
    "=MAX(R[-" & Rw & "]C:R[-1]C)"

'copy table down, 3 rows below original, and determine reference position
Range("A1").CurrentRegion.Copy _
    Destination:=Cells(Rw + 3, 1)
Set rngRef = Cells(Rw + 3, 1)
Set rngWhichCol = rngRef.Offset(Rw + 3, 0)
Set rngMaxVal = rngRef.Offset(Rw + 4, 0)
Set rngWhichPlayer = rngRef.Offset(Rw + 5, 0)
Set rngOutput = rngRef.Offset(Rw + 10, 0)

'write reference formulas and headings
rngRef.Offset(Rw + 1, 1).Resize(1, Col - 1).FormulaR1C1 = _
    "=LARGE(R[-21]C:R[-2]C,2)"
rngRef.Offset(Rw + 2, 1).Resize(1, Col - 1).FormulaR1C1 = _
    "=R[-2]C-R[-1]C"
rngRef.Offset(Rw + 3, 1).Resize(1, Col - 1).FormulaR1C1 = _
    "=RANK(R[-1]C,R[-1]C2:R[-1]C19,0)"
rngWhichCol.FormulaArray = "=MATCH(MIN(RC[1]:RC[18]),RC[1]:RC[18],0)"
rngWhichCol.Offset(1, 0).FormulaR1C1 = "=INDEX(R[-4]C[1]:R[-4]C[18],1,R[-1]C)"
rngWhichCol.Offset(2, 0).FormulaR1C1 = "=MATCH(R[-1]C,OFFSET(R[-" & Rw + 4 & "]C:R[-6]C,0,R[-2]C),0)"
rngOutput = "Position"
rngOutput.Offset(1, 0) = "Player"
rngOutput.Offset(2, 0) = "Score"

'loop through each position, prioritising on the position that has the most impact on the next choice.
For i = 1 To Col
    rngOutput.Offset(0, rngWhichCol.Value) = rngRef.Offset(0, rngWhichCol.Value)
    rngOutput.Offset(1, rngWhichCol.Value) = rngRef.Offset(rngWhichPlayer.Value, 0)
    rngOutput.Offset(2, rngWhichCol.Value) = rngRef.Offset(rngWhichPlayer.Value, rngWhichCol.Value)
    For j = 1 To Col - 1
        If j <> rngWhichCol.Value Then rngRef.Offset(rngWhichPlayer.Value, j) = 0
    Next j
    rngWhichCol.Offset(0, rngWhichCol.Value) = LOW_RANK
    ActiveSheet.Calculate
Next i

End Sub

Result using this approach: 1087.067
Not as good as Andrew's but another way to look at the problem...

Denis
 
Upvote 0
Thanks to you all, you have been really helpful. I just wish I could follow the coding more so I could understand it as well as appreciated your help.

I think Erik might be right about running all the permutations but that is a number to the power of 18, which is huge, even if it considers just the top 10 for each position it would be to the power of 14 :o

I'd like to see how close a complete run is to the ones Andrew has come up with, if the margin is close enough, it might just be easier to run with that.

Again Thank you to you all. :bow:
 
Upvote 0
Erik
just after your post of your code I posted a question on how to use your code as a straight cut and paste doesn't work. It is giving me an error something about not having info outside of subs.

Can you let me know how to use your code?
 
Upvote 0
Erik
just after your post of your code I posted a question on how to use your code as a straight cut and paste doesn't work. It is giving me an error something about not having info outside of subs.

Can you let me know how to use your code?
Oh, I didn't respond anymore since you seemed to have used Andrews code before I went back here.
It is code like any other code. Should start without errors. What did you do to make it run? Anyway it would run for years.
 
Upvote 0
Thanks Erik
I was interested in seeing how much better the result could get if all permutations were done.

BTW what is the best way to get better at VB?
 
Upvote 0
Thanks Erik
I was interested in seeing how much better the result could get if all permutations were done.

BTW what is the best way to get better at VB?

You can test it on smaller ranges. To my sense 10 rows x 8 columns is reasonable.

Range("B2:H8") < 1 sec
Range("B2:H12") < 12 sec
Range("B2:I12") < 1 minute
Range("B2:I15") about 20 minutes

you can enable these lines
Code:
Application.EnableCancelKey = xlErrorHandler
On Error GoTo skip
to exit the code using "escape"

the current maximum will be colored

best regards,
Erik
 
Upvote 0

Forum statistics

Threads
1,223,246
Messages
6,170,999
Members
452,373
Latest member
TimReeks

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top