用户将值输入一个范围-范围更改中的其余单元格,以使范围之和仍等于预设的数字(Sudoku Lite)

时间:2018-08-06 21:51:55

标签: excel excel-vba excel-formula

我正在尝试使用类似于sum函数的公式。唯一的区别是,一旦它累加了3个(或更多)单元格的数量,它将在编辑另一个单元格时编辑某个单元格以保持该总值。

例如:

A,B和C列分别为3,并且等于9:

| A  |  B  |  C |...| Total |
+----+-----+----+...+-------+
| 3  |  3  |  3 |...|   9   |

A列和B列分别被编辑为等于2,但是我仍然希望将总数保持为9,因此我希望C列自动更改为5。

此:

| A  |  B  |  C |...| Total |
+----+-----+----+...+-------+
| 2  |  2  |  3 |...|   7   |

应更新为:

| A  |  B  |  C |...| Total |
+----+-----+----+...+-------+
| 2  |  2  |  5 |...|   9   |

之所以这样做,是因为它被发送到该国的多个地区,在那里仅需编辑2个单元格,但我希望保持总数而不用编辑2k +行的数据输入。

我也接受VBA选项。有人有什么想法吗?

1 个答案:

答案 0 :(得分:1)

您可以找到问题here

示例:

A,B和C列分别为3,并且等于9:

| A  |  B  |  C |...| Total |
+----+-----+----+...+-------+
| 3  |  3  |  3 |...|   9   |

A列和B列分别被编辑为等于2,但是我仍然希望将总数保持为9,因此我希望C列自动更改为5:

| A  |  B  |  C |...| Total |
+----+-----+----+...+-------+
| 2  |  2  |  3 |...|   7   |

enter image description here

我意识到我的代码有些混乱,因此将其分解为sheet1,模块main和名为CollectionOfGeneratedValues的类。

  

您唯一需要在代码中调整的变量是masterRangerangeToFill中的列和sumTarget中的列以适合您的数据输入。

快速浏览:

  • 您必须在VBA中设置masterRange或要使用的范围。在电子表格中,您必须为sumtarget的每一行设置masterRange

  • 当在您的masterRange内的单元格中输入值时,我们将找出这是哪一行,并生成一个单独的范围即该行。

  • 如果输入的金额大于sumTarget,我们将Exit Sub并责骂用户。

  • 我们生成一个值的数组,其值以及用户输入将为sumtarget。然后,我们采用总和目标并减去用户输入。

    • 然后生成一个介于0和新的sumtarget.value之间的随机数
    • 然后我们存储该兰特编号,并从sumtarget中减去其值。
    • 我们columnsInRange进行了1次。
    • 当我们走出for loop作为最后一个值时,我们将该值设置为sumtarget剩余的值。
    • 对于通过上述步骤创建的集合,我们执行Fisher-Yates Shuffle,这样我们就不必始终将collection的值/电子表格的值降序

更新:感谢您的评论,建议和帮助。我应该多考虑一下什么样的形式。我只剩下某种奇怪的动态/静态混合动力。一个自定义的UserForm可以生成这些集合,然后可以打印到工作表中,这真是太酷了。无论如何,我接受了雷斯塔法里安的大部分建议。我喜欢用其中的一些方法,例如在哪里存储输入检查逻辑。但是总的来说,他的建议是正确的。再次谢谢你。

第1页:

Option Explicit
Private Sub Worksheet_Change(ByVal target As Range)
    SolveSudokuLite.Main target
End Sub

模块SolveSudokuLite

显式选项     子主要(ByRef目标为范围)     昏暗masterRange作为范围     昏暗rangeToFill作为范围     Dim valuesToFillRange作为GeneratedValuesCollection

        Application.EnableEvents = False
        Set masterRange = Range("B1:E5")
        Set valuesToFillRange = New GeneratedValuesCollection
        If Not Intersect(masterRange, target) Is Nothing Then
            TargetSum.setTargetSum target
            If Not IsValidInput(target) Then Exit Sub
            valuesToFillRange.GenerateValues target
            PrintValues valuesToFillRange, target
        End If
        Application.EnableEvents = True
End Sub

Function IsValidInput(ByVal target As Range) As Boolean
    IsValidInput = True
    If (target.value >= TargetSum.sum) Or Not IsNumeric(target.value) Then
        MsgBox ("WILL NOT CALCULATE FOR ROW " & target.Row & ", USER INPUT INVALID")
        IsValidInput = False
        Application.EnableEvents = True
    End If
End Function

Function PrintValues(ByRef valuesToFillRange As GeneratedValuesCollection, ByVal target As Range)
Dim rangeToFill                     As Range
Dim collectionCounter               As Long
Dim cellInRangeToFill               As Range

    Set rangeToFill = Range("A" & target.Row & ":E" & target.Row)
    collectionCounter = 1
    For Each cellInRangeToFill In rangeToFill
        If cellInRangeToFill.Address = target.Address Then
            cellInRangeToFill.value = target.value
        Else
            cellInRangeToFill.value = valuesToFillRange.Item(collectionCounter)
            collectionCounter = collectionCounter + 1
        End If
    Next cellInRangeToFill
End Function

名为GeneratedValuesCollection的类:

Option Explicit
Private GeneratedValuesCollection As Collection

Private Sub Class_Initialize()
    Set GeneratedValuesCollection = New Collection
End Sub

Private Sub Class_Terminate()
    Set GeneratedValuesCollection = Nothing
End Sub

Public Property Get Count() As Long
    Count = GeneratedValuesCollection.Count
End Property

Public Sub Add(num As Long)
    GeneratedValuesCollection.Add num
End Sub

Public Property Get Item(Index As Variant) As Long
     Item = GeneratedValuesCollection.Item(Index)
End Property

Public Sub Clear()
    Set GeneratedValuesCollection = New Collection
End Sub

Public Sub GenerateValues(ByVal target As Range)
Dim userSetValue                    As Long
Dim sumLeft                         As Long
Dim numbersToGenerate               As Long

    userSetValue = target.value
    sumLeft = SetInitialSumLeft(userSetValue)
    numbersToGenerate = NumberValuesToGenerate(target)
    SetValues numbersToGenerate, sumLeft
End Sub

Private Function SetInitialSumLeft(ByVal userSetValue As Long) As Long
    SetInitialSumLeft = TargetSum.sum - userSetValue
End Function

Private Function NumberValuesToGenerate(ByVal target As Range) As Long
Dim rangeToFill                     As Range

    Set rangeToFill = Range("A" & target.Row & ":E" & target.Row)
    NumberValuesToGenerate = rangeToFill.Columns.Count - 1
End Function

Private Sub SetValues(ByVal numbersToGenerate As Long, ByVal sumLeft As Long)
Dim counter                         As Long
Dim value                           As Long

    For counter = 1 To numbersToGenerate - 1
        value = Application.WorksheetFunction.RandBetween(0, sumLeft / 1.25)
        Me.Add value
        sumLeft = sumLeft - value
    Next counter
    Me.Add sumLeft
End Sub

Public Sub ShuffleCollection()
Dim holdValuesArray                As Collection

    Set holdValuesArray = DuplicateCollection()
    Swap holdValuesArray
End Sub

Private Function DuplicateCollection() As Collection
Dim counter                         As Long
Dim maxNum                          As Long

    Set DuplicateCollection = New Collection
    maxNum = Me.Count
    For counter = 1 To maxNum
        DuplicateCollection.Add Me.Item(counter)
    Next counter
End Function

Private Sub Swap(ByRef holdValuesArray As Collection)
Dim randomNum                       As Long
Dim maxNum                          As Long
Dim counter                         As Long

    Me.Clear
    maxNum = holdValuesArray.Count
    For counter = 1 To maxNum
        randomNum = Application.WorksheetFunction.RandBetween(1, holdValuesArray.Count)
        Me.Add (holdValuesArray(randomNum))
        holdValuesArray.Remove (randomNum)
    Next counter
End Sub

名为TargetSum的类:

Option Explicit
Private CollectionOfGeneratedValues As Collection

Private Type TTargetSum
        sum As Long
End Type

Private this As TTargetSum

Public Property Get sum() As Long
    sum = this.sum
End Property

Public Sub setTargetSum(ByVal value As Range)
    this.sum = Range("F" & value.Row)
End Sub