【问题标题】:Permutation to find the best teams possible排列以找到最好的团队
【发布时间】:2014-07-22 15:49:46
【问题描述】:

我正在尝试以不同的方式询问这个问题以获得一些回应。我一直在研究排列以找到对团队的最佳价值,但我似乎无法找到任何能让我清楚了解我需要做什么的材料.. 初学者在 excel 中创建排列表。

我想要完成的事情。我想获得可能的梦幻足球队的前 100 个排列(或更多排列)。

我不太确定如何设置它,因为我需要每个排列来包含每个位置,如下 QB、RB、RB、WR、WR、TE(6 个首发)。而且由于每个位置都有不同数量的球员,我不确定如何将所有这些放在一起,或者最好的方法是什么,所以我寻求帮助!

我想要的结果。

Position QB   RB   RB   WR   WR    TE   Total
Fantasy 350  110 115  220  120   125   1040

并在整个排列中继续使用上述格式,以找出可能的最佳团队。

玩家将来自不同的列,如下所示:

qb               rb
peyton 350   jamaal 235
drew   345

我不确定这是否可行,但我找到了一个宏,可以为我提供可能的位置结果(QB、RB、WR 等),但我似乎无法弄清楚如何组合这些点到位置并显示每个玩家在各自位置的所有排列。

再次感谢。

【问题讨论】:

  • 有人有什么想法或链接可以研究吗?
  • 我不确定你在寻找什么。如果玩家列对按玩家得分降序排列,则从每列对中挑选顶级玩家将为您提供最高可能的总数。我看不出排列与您的要求有什么关系。
  • 在梦幻足球中,通常允许您使用的最大积分数(类似于工资帽),因此您无法在每个位置选择最佳球员。如果不知道用户如何定义“顶级排列”,就很难回答这个问题
  • 我猜你可以忽略“顶部排列”请求。我最终可以从排列中查找表。但我想我想要的是我的六个首发球员可能的每一个可能的选秀结果……如果可能的话。将六个首发视为回合会更容易吗? 1-6 所以,最后我会有一个包含每个组合的桌子。就像如果你选择 QB 佩顿曼宁首先选择 350 分,然后选择第二选择 rb.. 第二个排列选择 RB 第一个选择获得 240 分,然后是 QB。

标签: excel permutation vba


【解决方案1】:

这不是算法的正确站点。它专门提供编程帮助。但是,我会尽力让你开始。请将以下每个步骤视为单独的任务。创建一个将执行任务 1 的宏。当它工作时,更新它以创建一个执行任务 1 和 2 的宏。我已尝试定义每个步骤,以便它是一个问题,您可以在必要时搜索答案。例如,对于第一步,在 Stackoverflow 中搜索“[excel-vba] Find last column”将给出相关问题和答案,展示了该任务最常用的技术。

我假设您有一个包含每个位置的球员的工作表。像这样的:

   A       B       C       D       E       F       G       H       ...
1  QB              RB              RB              WR              ...
2  Albert  100     Bernard 150     Charles 200     David   150
3  Eric    250     Fred    125     George  175     Ian     215
4      :               :                :              :               :                :              :                :

你说你目前有六个首发。这是否意味着一旦您了解了问题,您将添加更多玩家?无论哪种方式,首要任务都是确定职位数量。

对于六个位置,第 1 行的最后一个值将在第 11 列中,最后一列的值将是 12。对于不同的位置数,这些值将是 N 和 N+1,其中 N+1 是偶数位置数为(N+1)/2。

你说每个位置会有不同数量的玩家。任务 2 是识别和记录每个位置的球员人数。搜索“[excel-vba] Find last row”会给出多种技巧。

我会创建一个动态数组,例如:

Dim RowPlayerMax() As Long
ReDim RowPlayerMax(1 to NumPositions)

然后我会循环每个位置,并在RowPlayerMax(PositionNum) 中记录PositionNum*2 列的最后一行。

您会注意到我没有向您展示该循环的代码。这是一个程序员互相帮助开发的网站。我正在向您展示如何将您的需求分解为小步骤。如果我也向您展示 VBA,我将不会帮助您开发。您需要了解每个步骤的 VBA,如果您自己发现 VBA,对您的开发会更好。

您需要将每个位置的球员数据加载到内存中以便快速访问。我会将所有这些数据加载到 Variant 中,这将创建一个二维数组。第 1 行的第 1、3、5 列等将包含职位名称。第 2 行将包含第一组玩家姓名和分数。数组RowPlayerMax 将标识每个位置的最后一行。

我认为下一步是确定您希望生成排列的顺序。首先是最简单的序列。

您已经拥有数组RowPlayerMax。您需要另一个相同大小的数组:RowPlayerCrnt。你已经初始化了RowPlayerMax。您将 RowPlayerCrnt 初始化为 Player Per Position 表中第一个数据行的编号,即 2。为 RowPlayerMax 数组组成一些值,您有:

Element           1    2     3    4     5    6
RowPlayerMax     20    5    12    3    15    9
RowPlayerCrnt     2    2     2    2     2    2

然后您进入Do While True 循环。

Do 循环中的第一个任务是记录RowPlayerCrnt 定义的排列。我的示例数据是 Albert、Bernard、Charles、David 等,总共 100 + 150 + 200 + 100 ...。

Do 循环中的第二个任务是生成下一个排列。您需要一个从 1 到 NumPositions 或相反方向的 For 循环。我将从 1 转到 NumPositions

您查看每个位置并检查其当前值与最大值。如果某个位置的当前值小于最大值,则将其加一并退出For 循环。如果当前值等于最大值,则将其设置为第一个数据行并继续 For 循环。如果您退出 For 循环而不步进当前值,则您已经生成了每个排列。

考虑一下这意味着什么。第一个排列是2-2-2-2-2-2。在第一个循环中,根据最大值 (20) 检查位置 1 (2) 的当前值。由于 2 小于 20,因此将 1 添加到 2。第二个排列是 3-2-2-2-2-2。第三个排列是4-2-2-2-2-2,以此类推,直到20-2-2-2-2-2

对于20-2-2-2-2-2,位置 1 的当前值等于其最大值,因此将其设置为 2,循环继续考虑位置 2。位置 2 的当前值低于其最大值,因此将其加一.这给出了下一个排列为2-3-2-2-2-2

这将一直持续到排列为20-5-12-3-15-9。不可能增加这些当前值中的任何一个,因此所有排列都已生成。

您可能需要在纸上完成这项工作。一旦你掌握了正在发生的事情,你会发现这是一种生成每个排列的非常简单的方法。

如果您对这个排列顺序感到满意,那就没有什么可做的了。您需要从 Player per Position 表中提取信息并将其存储在您的 Permutations 表中。 Barrowc 说总数可能有一个最大值。如果这是正确的,您需要丢弃一些排列。

另一个可能的问题是,如果同一个球员可以踢多个位置。玩家 John 可能会出现在位置 1 或位置 2,但 John 出现在两个位置的排列必须被丢弃。

如果您打算生成所有可能的排列,然后按总数对它们进行排序,例如,生成顺序无关紧要。但是,如果您想生成前 100 个或 200 个,则顺序很重要。在这种情况下,您可能会对每个位置的玩家进行排序并寻求排列:

2-2-2-2-2-2
2-2-2-2-2-3
2-2-2-2-3-2
2-2-2-3-2-2
2-2-3-2-2-2
2-3-2-2-2-2
3-2-2-2-2-2
2-2-2-2-2-4
2-2-2-2-4-2
2-2-2-2-4-3
and so on.

在添加此序列的解释之前,我看到您的评论说您会对每个排列感到满意。无论如何,我可能已经给了你足够的思考。

编辑:附加建议和代码

在网上搜索“Excel VBA 教程”。有很多可供选择,因此请尝试一些并完成与您的学习风格相匹配的一个。我更喜欢书。我参观了一个不错的图书馆,借了几本 Excel VBA Primers,在家里试用,然后买了我喜欢的那个。我无法提出建议,因为它符合我的学习风格,而你的可能不同。

包含代码将完成我的回答,所以我已经这样做了。我创建了一个宏来执行步骤 1,然后创建第二个宏来执行步骤 1、2 和 3,依此类推,这是我向任何新手推荐的方法。当我不确定如何达到我所寻求的结果时,我会使用相同的技术。请注意,变量名称与上面的解释并不完全相同。当我创建整个宏而不是孤立的代码片段时,我觉得我原来的名字不太对。

我创建了一个包含两个工作表的工作簿:PlayerPerPosition 和 Permutations。如果您不喜欢我的名字,请更改 With Worksheets("xxx") 语句。我为 PlayerPerPosition 生成了一些数据,我认为这些数据足以代表您的数据:

Test1 确定列数并使用Debug.Print 将结果输出到即时窗口。当您打开 Visual Basic 编辑器时,立即窗口应该位于底部的右侧。如果缺少,请单击 Ctrl+G。这个宏演示了两种技术。我已经包含了 cmets 来解释我在做什么,但我没有解释 VBA。一旦您知道存在 VBA 语句,通常很容易查找它并找到带有示例的完整描述。如有必要,请询问,但您自己发现的越多,您发展技能的速度就越快。

Test2 确定并存储每个位置的最大行数。然后它将整个工作表加载为一个数组。对于Test1Test2,我使用Debug.Print 来输出结果。如果我为自己编写这些排列的生成,我会一口气编写宏,因为我对这项技术非常满意。但是我仍然会包含所有Debug.Print 声明。一个简单的错字。在步骤 1 或 2 中可能会导致细微的错误,从而可能导致后续步骤完全失败。在进行下一步之前检查每个步骤可以避免此类问题。

Macro Test3 以 2-2-2-2-2-2、3-2-2-2-2-2 等样式输出排列。根据我的样本数据,共有 62,208 个排列在我的笔记本电脑上生成 7 秒。没有进度指示器。我会用一个表格来表示进度,但我认为这些宏中有足够的想法,没有引入表格。

Macro Test4 输出在我的笔记本电脑上需要 18 秒才能生成的实际排列。

Option Explicit
Sub Test1()

  ' * Task 1 is to discover the number of columns in worksheet
  '   PlayerPerPosition. I demonstrate two techniques.
  ' * Technique 1 which uses the VBA equivalent of Ctrl+Left is
  '   the most popular technique judging from how often it is used
  '   in answers on this site.  However, like all other techniques,
  '   it does not work in every situation.
  ' * The Find method is the most widely applicable technique although,
  '   as the second example shows, you must be careful how you search.

  Dim ColPppMax As Long

  With Worksheets("PlayerPerPosition")

    ' Use VBA equivalenent of Ctrl+Left to find last value in row 1
    ColPppMax = .Cells(1, Columns.Count).End(xlToLeft).Column
    ' Maximum used column is one more than last column with a value in header row
    ColPppMax = ColPppMax + 1
    Debug.Print ColPppMax

    ' Use Find to find last column
    ' Note I am searching by columns
    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column
    Debug.Print ColPppMax

    ' See what happens if I search by rows
    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Column
    Debug.Print ColPppMax

  End With

End Sub
Sub Test2()

  ' * Task 2 is save the number of rows per position in worksheet
  '   PlayerPerPosition.  I use the VBA equivalement of Ctrl+Up.
  ' * Task 3 is to load the whole of worksheet PlayerPerPosition to
  '   an array.  For this I need to know the maximum used row for
  '   any position.

  Dim ColPppCrnt As Long
  Dim ColPppMax As Long
  Dim NumPosns As Long
  Dim PosnNumCrnt As Long
  Dim PppTable As Variant
  Dim RowPppMax() As Long
  Dim RowPppCrnt As Long
  Dim RowPppMaxMax As Long

  With Worksheets("PlayerPerPosition")

    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column

    NumPosns = ColPppMax / 2       ' I ought to check there are an even number of columns

    ReDim RowPppMax(1 To NumPosns)

    RowPppMaxMax = 0
    PosnNumCrnt = 1
    For ColPppCrnt = 2 To ColPppMax Step 2
      RowPppMax(PosnNumCrnt) = .Cells(Rows.Count, ColPppCrnt).End(xlUp).Row
      If RowPppMaxMax < RowPppMax(PosnNumCrnt) Then
        ' If this position has more rows than any previous position, record new maximum row
        RowPppMaxMax = RowPppMax(PosnNumCrnt)
      End If
      PosnNumCrnt = PosnNumCrnt + 1
    Next

    'Output maximum row for each column
    Debug.Print "Position ";
    For PosnNumCrnt = 1 To NumPosns
      Debug.Print Right("   " & PosnNumCrnt, 3);
    Next
    Debug.Print
    Debug.Print " Max Row ";
    For PosnNumCrnt = 1 To NumPosns
      Debug.Print Right("   " & RowPppMax(PosnNumCrnt), 3);
    Next
    Debug.Print

    ' Load worksheet to variant as two-dimensional array
    PppTable = .Range(.Cells(1, 1), .Cells(RowPppMaxMax, ColPppMax)).Value

  End With

  ' Display Players Per Position table
  For RowPppCrnt = 1 To RowPppMaxMax
    For ColPppCrnt = 1 To ColPppMax Step 2
      ' Assume maximum of six characters per player name and
      ' maximum of 9999 for number of points
      Debug.Print Left(PppTable(RowPppCrnt, ColPppCrnt) & Space(6), 6) & " " & _
                  Right("   " & PppTable(RowPppCrnt, ColPppCrnt + 1), 3) & " ";
    Next
    Debug.Print
  Next

End Sub
Sub Test3()

  ' This macro generates the indices into the Ppp Table from which the
  ' actual permutations will be generated.

  ' If you have multi-row headers, using constants makes the code easy to change.
  Const RowPppDataFirst As Long = 2

  Dim ColPppCrnt As Long
  Dim ColPppMax As Long
  Dim GenerationFinished As Boolean
  Dim NumPosns As Long
  Dim PermStr As String
  Dim PosnNumCrnt As Long
  Dim PppTable As Variant
  Dim RowPermCrnt As Long
  Dim RowPppCrnt() As Long
  Dim RowPppMax() As Long
  Dim RowPppMaxMax As Long
  Dim TimeStart As Single

  TimeStart = Timer   ' Seconds since midnight

  ' Stops screen flash and speeds up macro when writing to worksheet
  Application.ScreenUpdating = False

  With Worksheets("PlayerPerPosition")

    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column

    NumPosns = ColPppMax / 2       ' I ought to check there are an even number of columns

    ReDim RowPppMax(1 To NumPosns)

    RowPppMaxMax = 0
    PosnNumCrnt = 1
    For ColPppCrnt = 2 To ColPppMax Step 2
      RowPppMax(PosnNumCrnt) = .Cells(Rows.Count, ColPppCrnt).End(xlUp).Row
      If RowPppMaxMax < RowPppMax(PosnNumCrnt) Then
        ' If this position has more rows than any previous position, record new maximum row
        RowPppMaxMax = RowPppMax(PosnNumCrnt)
      End If
      PosnNumCrnt = PosnNumCrnt + 1
    Next

    PppTable = .Range(.Cells(1, 1), .Cells(RowPppMaxMax, ColPppMax)).Value

  End With

   ' Initialise current row table
  ReDim RowPppCrnt(1 To NumPosns)
  For PosnNumCrnt = 1 To NumPosns
    RowPppCrnt(PosnNumCrnt) = RowPppDataFirst
  Next

  RowPermCrnt = 1

  With Worksheets("Permutations")

    Do While True

      ' Output current permutation
      PermStr = RowPppCrnt(1)
      For PosnNumCrnt = 2 To NumPosns
        PermStr = PermStr & "-" & RowPppCrnt(PosnNumCrnt)
      Next
      .Cells(RowPermCrnt, 1).Value = PermStr
      RowPermCrnt = RowPermCrnt + 1

      ' Generate next permulation index
      GenerationFinished = True     ' Assume finishe until find otherwise
      For PosnNumCrnt = 1 To NumPosns
        If RowPppCrnt(PosnNumCrnt) < RowPppMax(PosnNumCrnt) Then
          RowPppCrnt(PosnNumCrnt) = RowPppCrnt(PosnNumCrnt) + 1
          GenerationFinished = False
          Exit For
        End If
        RowPppCrnt(PosnNumCrnt) = RowPppDataFirst
      Next
      If GenerationFinished Then
        Exit Do
      End If

   Loop

  End With

  Debug.Print "Duration " & Format(Timer - TimeStart, "##0.00")

End Sub
Sub Test4()

  ' This macro generates actual permutations.

  Const RowPppPosnName As Long = 1
  Const RowPppDataFirst As Long = 2

  Dim ColPppCrnt As Long
  Dim ColPppMax As Long
  Dim GenerationFinished As Boolean
  Dim NumPosns As Long
  Dim PointsTotal As Long
  Dim PosnNumCrnt As Long
  Dim PppTable As Variant
  Dim RowPermCrnt As Long
  Dim RowPppCrnt() As Long
  Dim RowPppMax() As Long
  Dim RowPppMaxMax As Long
  Dim TimeStart As Single

  TimeStart = Timer   ' Seconds since midnight

  Application.ScreenUpdating = False

  With Worksheets("PlayerPerPosition")

    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column

    NumPosns = ColPppMax / 2       ' I ought to check there are an even number of columns

    ReDim RowPppMax(1 To NumPosns)

    RowPppMaxMax = 0
    PosnNumCrnt = 1
    For ColPppCrnt = 2 To ColPppMax Step 2
      RowPppMax(PosnNumCrnt) = .Cells(Rows.Count, ColPppCrnt).End(xlUp).Row
      If RowPppMaxMax < RowPppMax(PosnNumCrnt) Then
        ' If this position has more rows than any previous position, record new maximum row
        RowPppMaxMax = RowPppMax(PosnNumCrnt)
      End If
      PosnNumCrnt = PosnNumCrnt + 1
    Next

    PppTable = .Range(.Cells(1, 1), .Cells(RowPppMaxMax, ColPppMax)).Value

  End With

   ' Initialise current row table
  ReDim RowPppCrnt(1 To NumPosns)
  For PosnNumCrnt = 1 To NumPosns
    RowPppCrnt(PosnNumCrnt) = RowPppDataFirst
  Next

  With Worksheets("Permutations")

    .Cells.EntireRow.Delete     ' Delete any previous output

    ' Generate header row
    RowPermCrnt = 1
    PosnNumCrnt = 1   ' Uses as column number for Permutations worksheets
    For ColPppCrnt = 1 To ColPppMax Step 2
      .Cells(RowPermCrnt, PosnNumCrnt).Value = PppTable(RowPppPosnName, ColPppCrnt)
      PosnNumCrnt = PosnNumCrnt + 1
    Next
    With .Cells(RowPermCrnt, NumPosns + 1)
      .Value = "Total"
      .HorizontalAlignment = xlRight
    End With
    .Range(.Cells(1, 1), .Cells(1, NumPosns + 1)).Font.Bold = True

    RowPermCrnt = RowPermCrnt + 1

    Do While True

      ' Output current permutation
      PointsTotal = 0
      ColPppCrnt = 1
      For PosnNumCrnt = 1 To NumPosns
        .Cells(RowPermCrnt, PosnNumCrnt).Value = PppTable(RowPppCrnt(PosnNumCrnt), ColPppCrnt)
        ColPppCrnt = ColPppCrnt + 1
        PointsTotal = PointsTotal + PppTable(RowPppCrnt(PosnNumCrnt), ColPppCrnt)
        ColPppCrnt = ColPppCrnt + 1
      Next
      .Cells(RowPermCrnt, NumPosns + 1).Value = PointsTotal
      RowPermCrnt = RowPermCrnt + 1

      ' Generate next permulation index
      GenerationFinished = True     ' Assume finishe until find otherwise
      For PosnNumCrnt = 1 To NumPosns
        If RowPppCrnt(PosnNumCrnt) < RowPppMax(PosnNumCrnt) Then
          RowPppCrnt(PosnNumCrnt) = RowPppCrnt(PosnNumCrnt) + 1
          GenerationFinished = False
          Exit For
        End If
        RowPppCrnt(PosnNumCrnt) = RowPppDataFirst
      Next
      If GenerationFinished Then
        Exit Do
      End If

   Loop

  End With

  Debug.Print "Duration " & Format(Timer - TimeStart, "##0.00")

End Sub

【讨论】:

  • 这太棒了,托尼!我是 vba 的初学者,但我对循环和简单项目有基本的了解,但这个问题似乎让我不知从何开始以及如何完成。我感谢您在此答案中付出的努力,这肯定会让我继续前进,感谢您对每个步骤的解释,这将有助于将想法集中在一起。
  • 附注。我的背景真的不是编码,但我一直很感兴趣,并一直在努力扩展我对这个主题的了解。话虽如此,您是否碰巧知道您发现的任何网站或有用的材料作为有用的学习工具或示例?
  • @ExcelNoobie25 我添加了一个新部分,希望对您有所帮助。
  • 这太棒了!..我真的还有很长的路要走。感谢您所有的帮助。 @托尼
  • @ExcelNoobie25。 user3879000 提出了一些很好的观点。我的例程以方便算法的顺序生成所有可能的排列。在我的第一部分结束时,我讨论了丢弃不需要的排列,但我的代码没有使用任何这些技术。我的代码不允许同一个球员同时担任多个职位。我对美式足球知之甚少,但我可以做出一些改变来消除一些,也许是所有提到的问题。这会有所帮助还是我已经给了你足够的思考?
【解决方案2】:

我去年做了一个简单的版本。 如果您运行标准阵容(1QB、2RB、3WR、1TE)的组合,则在重复移除后您将获得 420 个组合。

这些组合是严格的,因为它们不会“适应”实时草稿。我将 ADP 用于我希望在某个选秀职位上可用的人。同样,当你的队友在 ADP 之前获得选秀权时,它不会针对意外的选秀权进行调整。

假设我在 10 支球队的联赛中排名第 9。我在选择 1.9、2.2、3.9、4.2 等处手动输入每个位置的值 这听起来很乏味,但您会开始看到模式,并且需要大约一个小时才能完成。然后,您可以对列求和。

我不明白你们乱说的行话。但是如果你能把这个调整为现场选秀,你将摧毁任何休闲联赛。换句话说,你真的在​​做某事(我怀疑这就是 Draft Dominator 应用程序的工作方式)。

至少,你会从你的努力中学到一些东西。例如,我的电子表格中的大多数最佳组合都告诉我在第 5 轮,即在他的 ADP 之前 1-2 轮选择安东尼奥·布朗。伙计,如果你有一个这样的球员,那将是值得你付出时间和精力的。

【讨论】:

  • 你是如何进行组合的。托尼经历的一个很好的开始,但我注意到了两个缺点,我正在尝试学习代码以及如何调整它,但我遇到的第一个问题是,因为列是如果两个跑卫在同一列中,我无法在同一行中获得排列的位置。第二,我已经将我的玩家减少了很多,但是当它试图循环遍历所有排列时,excel 似乎用完了行。
  • 听起来你已经完成了我的主要目标。我正在寻找类似的东西,但我仍然是 excel vba 的新手,缺乏快速抛出的诀窍像这样的东西在一起。 @user3879000
  • 我仍然在为我到底是如何做到这一点而绞尽脑汁的。从相同的概念开始,我可以制作多少种不同的字母 D、O、G 组合?除了我使用了 Q、R、R、W、W、W、T。 IIRC 出现了 4 或 5 千种组合。但是由于每个 W 在同一轮中不是唯一的,因此您可以删除所有重复(我也忘记了如何做,让您了解我是什么菜鸟)。在前 7 轮中,我得出了 420 个可能的结果。
  • 这比你们所做的要简单得多,但这就是我要开始的方式。下载在这个链接...ne.jp/asahi/math.edu/ami/myprog/jk_eng.htm
  • 我为 PPR 评分运行了我的数据。脱颖而出的 2 名球员是 Cordarelle Patterson 和 Joique Bell。去年在这个数据中脱颖而出的人是安东尼奥·布朗,所以我希望有类似的运气。 Draftmas 今天适合我。祝我好运!
【解决方案3】:

编辑 2 - 响应和更多代码

回应

“另外,你知道你是否可以从 4 列中取出位置并从中进行 6 位置排列?”是的,但我认为不值得。

我假设你想要这个,因为球员可以被安排在 RB 和 WR 位置。 (1) 我以不同的方式允许这样做,我认为这提供了更大的灵活性。 (2) 这个要求会为每个排列的每个位置增加一个步骤。

如果您不同意,这就是您实现所要求的效果的方式。目前,宏有以下内容:

Position    QB     RB     RB     WR     WR     TE
Column      1      2      3      4      5      6
PppTable

Position    QB     RB     RB     WR     WR     TE
Column      1      2      3      4      5      6
RowPppCrnt  a      b      c      d      e      f

数组RowPppCrnt 标识从数组PppTable 中选择哪些行。 a、b、c、d、e 和 f 是PppTable 中的行号,RowPppCrnt 中的列号与PppTable 中的列号匹配。

下面,PppTable 只有 4 列,而 RowPppCrnt 仍然有 6 个条目。新数组SwitchColRowPppCrnt 中的列与PppTable 中的列相关联。例如,RowPppCrnt 中的第 6 列,SwitchCol 中的第 6 列表示转到 PppTable 中的第 4 列。

Position    QB     RB     WR     TE
Column      1      2      3      4
PppTable

Position    QB     RB     RB     WR     WR     TE
Column      1      2      3      4      5      6
SwitchCol   1      2      2      3      3      4
RowPppCrnt  a      b      c      d      e      f

更多代码

在宏Test2 中,我介绍了将范围加载到数组中的功能,从而加快访问速度。反之亦然。对于宏 Test5,我添加了一个二维数组 PermTable,其中包含 200 行,其中我累积了最好的 200 个排列。

我引入了一个新常量Const PointsTotalMaxPermitted As Long = 1000。点值高于此最大值的排列将被拒绝。如果您不想要此功能,请将 1000 替换为一些不可能的高值。否则,将 1000 替换为任何合适的限制。

我已经引入了对同一玩家处于多个位置的检查。对于我的测试数据,我使两个 RB 列和两个 WR 列相同。这就是我提出您问题背后的要求的方式。这种方法的缺点是数据重复。优点是它不需要额外的代码,并且它允许玩家可以在多个位置上玩。

如果一个排列通过了上述两个测试,则考虑将其添加到 PermTable。如果排列是前 200 个之一,则始终添加。如果它不在前 200 个范围内,并且它的分值大于最低值的分值,现有排列,旧排列被新排列覆盖。

只有在考虑了所有排列后,才会将PermTable 写入工作表。宏 Test5Test4 快得多,因为它向工作表写入的数据非常少。 Test4 在 18 秒内处理 62,208 个排列。 Test5 在 4 秒内处理 1,080,000 个排列。

在下面的代码中,我保留了我的诊断代码,但已将其注释掉。如果您想尝试该代码,则必须添加它使用的三个工作表。

Sub Test5()

  ' This macro saves the 200 permulations with the highest permitted totals.

  Const RowPppPosnName As Long = 1
  Const RowPppDataFirst As Long = 2
  Const PointsTotalMaxPermitted As Long = 1000

  Dim ColPppCrnt As Long
  Dim ColPppMax As Long
  Dim GenerationFinished As Boolean
  Dim NumPermsGenerated As Long
  Dim NumPosns As Long
  Dim PermCrnt() As Variant
  Dim PermCrntIsValid As Boolean
  Dim PermTable() As Variant
  Dim PointsTotalCrnt As Long
  Dim PointsTotalLowest As Long
  Dim PosnNumCrnt1 As Long
  Dim PosnNumCrnt2 As Long
  Dim PppTable As Variant
  'Dim RowNotTop200Crnt As Long
  Dim RowPermCrnt As Long
  Dim RowPermCrntMax As Long
  Dim RowPermLowestTotal As Long
  Dim RowPppCrnt() As Long
  Dim RowPppMax() As Long
  Dim RowPppMaxMax As Long
  'Dim RowRepeatCrnt As Long
  'Dim RowTooHighCrnt As Long
  Dim TimeStart As Single

  TimeStart = Timer   ' Seconds since midnight

  Application.ScreenUpdating = False

  With Worksheets("PlayerPerPosition")

    ColPppMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column

    NumPosns = ColPppMax / 2       ' I ought to check there are an even number of columns

    ReDim RowPppMax(1 To NumPosns)

    RowPppMaxMax = 0
    PosnNumCrnt1 = 1
    For ColPppCrnt = 2 To ColPppMax Step 2
      RowPppMax(PosnNumCrnt1) = .Cells(Rows.Count, ColPppCrnt).End(xlUp).Row
      If RowPppMaxMax < RowPppMax(PosnNumCrnt1) Then
        ' If this position has more rows than any previous position, record new maximum row
        RowPppMaxMax = RowPppMax(PosnNumCrnt1)
      End If
      PosnNumCrnt1 = PosnNumCrnt1 + 1
    Next

    PppTable = .Range(.Cells(1, 1), .Cells(RowPppMaxMax, ColPppMax)).Value

  End With

   ' Initialise current row table
  ReDim RowPppCrnt(1 To NumPosns)
  For PosnNumCrnt1 = 1 To NumPosns
    RowPppCrnt(PosnNumCrnt1) = RowPppDataFirst
  Next

  ' Size arrays to hold current permutation prior to validation and
  ' the 200 permutation with the highest permitted totals.
  ' Note with 2D arrays it is conventional for the first dimension to
  ' be for columns and the second dimension to be for rows.  Arrays
  ' holded from ranges or to be loaded to ranges are the other way
  ' round.
  ReDim PermCrnt(1 To NumPosns)
  ReDim PermTable(1 To 200, 1 To NumPosns + 1)     ' Extra column for total

  NumPermsGenerated = 0
  RowPermCrntMax = 0
  'RowTooHighCrnt = 0
  'RowRepeatCrnt = 0
  'RowNotTop200Crnt = 0

  'Worksheets("Too High").Cells.EntireRow.Delete     ' Delete any previous output
  'Worksheets("Repeat").Cells.EntireRow.Delete
  'Worksheets("Not Top 200").Cells.EntireRow.Delete
  'Worksheets("Permutations").Cells.EntireRow.Delete

  Do While True

    ' Generate current permulation from indices
    PermCrntIsValid = True  ' Assume current permutation is valid until find otherwise
    PointsTotalCrnt = 0
    ColPppCrnt = 1
    For PosnNumCrnt1 = 1 To NumPosns
      PermCrnt(PosnNumCrnt1) = PppTable(RowPppCrnt(PosnNumCrnt1), ColPppCrnt)
      ColPppCrnt = ColPppCrnt + 1
      PointsTotalCrnt = PointsTotalCrnt + PppTable(RowPppCrnt(PosnNumCrnt1), ColPppCrnt)
      ColPppCrnt = ColPppCrnt + 1
    Next
    NumPermsGenerated = NumPermsGenerated + 1

    ' Check points total not higher than maximum
    If PointsTotalCrnt > PointsTotalMaxPermitted Then
      PermCrntIsValid = False
      'RowTooHighCrnt = RowTooHighCrnt + 1
      'If RowTooHighCrnt < 65537 Then
      '  With Worksheets("Too High")
      '    For PosnNumCrnt1 = 1 To NumPosns
      '      .Cells(RowTooHighCrnt, PosnNumCrnt1) = PermCrnt(PosnNumCrnt1)
      '    Next
      '    .Cells(RowTooHighCrnt, NumPosns + 1) = PointsTotalCrnt
      '  End With
      'End If
    End If

    ' Check player not repeated
    If PermCrntIsValid Then
      For PosnNumCrnt1 = 1 To NumPosns - 1
        For PosnNumCrnt2 = PosnNumCrnt1 + 1 To NumPosns
          If PermCrnt(PosnNumCrnt1) = PermCrnt(PosnNumCrnt2) Then
            ' Same player in two positions
            PermCrntIsValid = False
            Exit For
          End If
        Next
        If Not PermCrntIsValid Then
          'RowRepeatCrnt = RowRepeatCrnt + 1
          'If RowRepeatCrnt < 65537 Then
          '  With Worksheets("Repeat")
          '    For PosnNumCrnt2 = 1 To NumPosns
          '      .Cells(RowRepeatCrnt, PosnNumCrnt2) = PermCrnt(PosnNumCrnt2)
          '    Next
          '    .Cells(RowRepeatCrnt, NumPosns + 1) = PointsTotalCrnt
          '  End With
          'End If
          Exit For
        End If
      Next
    End If

    If PermCrntIsValid Then
      If RowPermCrntMax < UBound(PermTable, 1) Then
        ' Permutations table is not full so save current permulation in
        ' next available row.
        RowPermCrntMax = RowPermCrntMax + 1
        For PosnNumCrnt1 = 1 To NumPosns
          PermTable(RowPermCrntMax, PosnNumCrnt1) = PermCrnt(PosnNumCrnt1)
        Next
        PermTable(RowPermCrntMax, NumPosns + 1) = PointsTotalCrnt
        If RowPermCrntMax = 1 Then
          ' This is first permutation to be saved.  Record as lowest
          PointsTotalLowest = PointsTotalCrnt
          RowPermLowestTotal = RowPermCrntMax
        Else
          ' Check for new lowest total
          If PointsTotalLowest > PointsTotalCrnt Then
            PointsTotalLowest = PointsTotalCrnt
            RowPermLowestTotal = RowPermCrntMax
          End If
          If RowPermCrntMax = UBound(PermTable, 1) Then
            ' Have just filled Permutations table
            With Worksheets("Permutations")
             .Range(.Cells(1, 1), _
                    .Cells(UBound(PermTable, 1), NumPosns + 1)).Value = PermTable
            End With
          End If
        End If
      Else
        ' Permutations table is full so only save current permulation
        ' if its points total is greater than lowest in table
        If PointsTotalCrnt > PointsTotalLowest Then
          ' Replace permutation with lowest total with with current permutation
          For PosnNumCrnt1 = 1 To NumPosns
            PermTable(RowPermLowestTotal, PosnNumCrnt1) = PermCrnt(PosnNumCrnt1)
          Next
          PermTable(RowPermLowestTotal, NumPosns + 1) = PointsTotalCrnt
          ' Find new lowest total
          ' Initialise lowest from first row in table then search rest of table
          PointsTotalLowest = PermTable(1, NumPosns + 1)
          RowPermLowestTotal = 1
          For RowPermCrnt = 2 To UBound(PermTable, 1)
            If PointsTotalLowest > PermTable(RowPermCrnt, NumPosns + 1) Then
              PointsTotalLowest = PermTable(RowPermCrnt, NumPosns + 1)
              RowPermLowestTotal = RowPermCrnt
            End If
          Next
        Else
          'RowNotTop200Crnt = RowNotTop200Crnt + 1
          'If RowNotTop200Crnt < 65537 Then
          '  With Worksheets("Not Top 200")
          '    For PosnNumCrnt1 = 1 To NumPosns
          '      .Cells(RowNotTop200Crnt, PosnNumCrnt1) = PermCrnt(PosnNumCrnt1)
          '    Next
          '    .Cells(RowNotTop200Crnt, NumPosns + 1) = PointsTotalCrnt
          '    .Cells(RowNotTop200Crnt, NumPosns + 2) = PermTable(RowPermLowestTotal, NumPosns + 1)
          '  End With
          'End If
        End If  ' Current permutation to replace lowest
      End If  ' Permutation table full
    End If  ' PermCrntIsValid

    ' Generate next permulation index
    GenerationFinished = True     ' Assume finishe until find otherwise
    For PosnNumCrnt1 = 1 To NumPosns
      If RowPppCrnt(PosnNumCrnt1) < RowPppMax(PosnNumCrnt1) Then
        RowPppCrnt(PosnNumCrnt1) = RowPppCrnt(PosnNumCrnt1) + 1
        GenerationFinished = False
        Exit For
      End If
      RowPppCrnt(PosnNumCrnt1) = RowPppDataFirst
    Next
    If GenerationFinished Then
      Exit Do
    End If

  Loop  ' until all permutation have been generated

  With Worksheets("Permutations")

    .Cells.EntireRow.Delete     ' Delete any previous output

    ' Generate header row
    RowPermCrnt = 1
    PosnNumCrnt1 = 1   ' Uses as column number for Permutations worksheets
    For ColPppCrnt = 1 To ColPppMax Step 2
      .Cells(RowPermCrnt, PosnNumCrnt1).Value = PppTable(RowPppPosnName, ColPppCrnt)
      PosnNumCrnt1 = PosnNumCrnt1 + 1
    Next
    With .Cells(RowPermCrnt, NumPosns + 1)
      .Value = "Total"
      .HorizontalAlignment = xlRight
    End With
    .Range(.Cells(1, 1), .Cells(1, NumPosns + 1)).Font.Bold = True

    RowPermCrnt = RowPermCrnt + 1

     ' Write Permutation table to worksheet
    .Range(.Cells(2, 1), _
           .Cells(UBound(PermTable, 1) + 1, NumPosns + 1)).Value = PermTable

  End With

  Debug.Print "Duration " & Format(Timer - TimeStart, "##0.00")
  Debug.Print "Number of permutations generated " & NumPermsGenerated

End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多