【问题标题】:An algorithm for iteration over all values in a column of a table一种迭代表列中所有值的算法
【发布时间】:2015-08-28 17:40:55
【问题描述】:

我正在寻找一种适用于下表的简单算法:

在第一列中,您会看到约束。算法应该使用第二列来输出迭代,应该像这样完成:

 0  0  0
 0  0  1
........
 0  0 29
 0  1  0
........
 0  1 29
 0  2  0
 0  2  1
........
........
27  9 29
28  0  0
........
........
28  9 29

目前我有以下代码:

Dim wksSourceSheet As Worksheet
Set wksSourceSheet = Worksheets("Solver")

Dim lngLastRow As Long
Dim lngLastColumn As Long

With wksSourceSheet 
    lngLastRow = IIf(IsEmpty(.Cells(.Rows.Count, 1)), _
        .Cells(.Rows.Count, 1).End(xlUp).Row, .Rows.Count)
    lngLastColumn = IIf(IsEmpty(.Cells(1, .Columns.Count)), _
        .Cells(1, .Columns.Count).End(xlToLeft).Column, .Columns.Count)

    Dim intRowOuter As Integer
    Dim intRowInner As Integer
    For intRowOuter = 2 To lngLastRow
        .Cells(intRowOuter, lngLastColumn).Value = 0
    Next intRowOuter
    For intRowOuter = lngLastRow To 2 Step -1
        For intRowInner = lngLastRow To intRowOuter Step -1
            Dim constraint As Integer
            Dim intConstraintCounter As Integer
            intConstraint = .Cells(intRowInner, 1)
            For intConstraintCounter = 1 To intConstraint 
                .Cells(intRowInner, lngLastColumn).Value = intConstraintCounter
            Next intStampCounter
        Next intRowInner
    Next intRowOuter
End With

这可能是正确的方法,但有些地方不正确。不幸的是,我被卡住了,所以我希望能得到一些帮助来解决这个问题。

【问题讨论】:

  • 一共有多少位数?六个如例子所示?
  • 你试过什么?你在想什么?如果您没有解决方案也可以,但您必须知道如何解决它。

标签: algorithm vba loops iteration brute-force


【解决方案1】:

解决方案

我建议使用一个数组来存储约束,一个来表示计数器。

Dim MaxNum() As Long
Dim myCounter() As Long
ReDim MaxNum(1 To NumDigits)
ReDim myCounter(1 To NumDigits)

接下来需要初始化MaxNum。这可能涉及遍历包含约束的单元格。比如:

Dim constraintRange As Range
Dim i As integer

Set constraintRange = wksSourceSheet.Range("A2:A4")
For i = 1 to numDigits
    MaxNum(i) = constraintRange.Cells(i,1).Value
Next i

现在我们只需要编写一个递增计数器函数!这个想法很简单,我们只是从最低有效位到最高位。我们增加 LSD,如果有溢出,我们将其设置为 0,然后将 1 加到下一位。它看起来像这样:

Sub IncrNum(ByRef myNum() As Long, ByRef MaxNum() As Long) 
    Dim i As Integer
    For i = LBound(myNum) To UBound(myNum)
        myNum(i) = myNum(i) + 1
        If myNum(i) > MaxNum(i) Then 'Overflow!
            myNum(i) = 0 'Reset digit to 0 and continue 
        Else
            Exit For 'No overflow so we can just exit
        End If
    Next i
End Sub

这只是一个 for 循环!我认为这将是最干净的解决方案:)

注意:要使用此功能,您只需执行IncrNum(myCounter, MaxNum)。这会将myCounter 的值更改为序列中的下一个。从这里你可以通过dstRange = myCounter 粘贴到一个范围。

测试

在我自己的测试中,我使用了一个 while 循环来打印出所有的值。它看起来像这样:

Do While Not areEqual(MaxNum, myCounter)
    Call IncrNum(myCounter,MaxNum)
    outRange = myCounter
    Set outRange = outRange.Offset(1, 0)
Loop

areEqual 只是一个函数,如果参数包含相同的值,则返回 true。如果您愿意,我可以提供我的代码,否则我会忽略它,以使我的答案尽可能正常。

【讨论】:

  • 我正在尝试根据我的应用调整您的解决方案。首先,我想知道您正在调用您的计数器功能,例如:IncrNum(myCounter,MaxNum)。在这种情况下我得到语法错误。我查看了manual,发现应该像这样调用子:Call IncrNum(myCounter,MaxNum)
  • 第二,你说你只使用了一个for循环。但是由于计数器函数仅在我需要将此函数放在另一个循环中时才会增加计数器。此外,我需要实现函数areEqual,它比较两个数组,我无法想象没有第三个循环如何做到这一点。所以我们实际上需要3个循环。我做对了吗?
  • 要将数组myCounter 写入相应的单元格,我需要另一个循环。
  • 如示例中所示,您只需执行outRange = myCounter 即可将数组写入范围。如果您需要从0MaxNum 的每个数字,则需要对每个可能的数字进行一次迭代;这是无法避免的。请注意,IncrNum 循环大部分时间都会提前中断。这比你的循环更有效。最后,如果您在制作areEqual 时做出了一些聪明的决定,您可以在大多数情况下让它在第一次迭代时退出。这将在很大程度上是恒定时间操作!
  • 谢谢,你已经给了我很大的帮助!我选择使用计数器函数的循环输出到表,因为该方法对性能的影响最小。
【解决方案2】:

也许可以修改类似的内容以满足您的需求。它模拟进位加法:

Sub Clicker(MaxNums As Variant)
    Dim A As Variant
    Dim i As Long, j As Long, m As Long, n As Long
    Dim sum As Long, carry As Long
    Dim product As Long

    m = LBound(MaxNums)
    n = UBound(MaxNums)
    product = 1
    For i = m To n
        product = product * (1 + MaxNums(i))
    Next i
    ReDim A(1 To product, m To n)

    For j = m To n
        A(1, j) = 0
    Next j

    For i = 2 To product
        carry = 1
        For j = n To m Step -1
            sum = A(i - 1, j) + carry
            If sum > MaxNums(j) Then
                A(i, j) = 0
                carry = 1
            Else
                A(i, j) = sum
                carry = 0
            End If
        Next j
    Next i
    Range(Cells(1, 1), Cells(product, n - m + 1)).Value = A
End Sub

像这样使用:

Sub test()
    Clicker Array(3, 2, 2)
End Sub

产生:

【讨论】:

  • 这似乎是一个很好的解决方案,只要 product 适合 Long!请注意,您可以通过在进位为 0 时退出内部 for 循环来进行轻微优化,就像我在回答中所做的那样:)
  • @MichaelSPriz 我注意到我们几乎同时发布了非常相似的解决方案。细节可能会有所不同,但沿着这条线的东西显然是要走的路。我保持内部循环保持原样,因为我仍然需要从它上面的行中提取值。
  • 非常真实!我错过了那一点。感谢您的澄清:)
【解决方案3】:

x%10x Mod 10 给出 x 除以 10 时的余数,这样你就可以得到 x 的最后一位数。

由于您的问题是特别要求每个数字不超过 463857。您可以让计数器从 000000 递增到 463857,并且仅输出/使用满足以下条件的数字:

IF(x%10 <= 7 AND x%100 <=57 AND x%1000 <= 857 AND x%10000 <=3857 AND x%100000 <= 63857 AND x <= 463857) 
THEN //perform task.

【讨论】:

  • 好吧,我会接受每种语言的解决方案,甚至是伪代码。这是关于算法,而不是实现。
  • 是的,他刚刚编辑了问题..在他要求任何语言的伪代码示例之前。我正在编辑我的答案。
  • 但我不确定如何将模运算符应用于我的案例。
  • 很遗憾,您的提议行不通。我提到了术语“数字”和“数字”以简化对我问题的理解。在电子表格中,我有“单元格”和“数字”,因此约束可以 >= 10。
  • 我建议编辑您的问题来解释这一点。那么数字的每个“数字”都在不同的单元格中?而且每个“数字”都可以大于10?
猜你喜欢
  • 2020-04-28
  • 2020-10-20
  • 2017-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-21
  • 2011-01-06
  • 2020-02-07
相关资源
最近更新 更多