Выбрать случайную ячейку в диапазоне

Я пытаюсь выполнить действие в VBA в диапазоне ячеек. Я хотел бы, чтобы выбор ячеек был случайным, а не в порядке настройки диапазона.

Sub Solver_Step_Evo()
    Set Rng = GetRange(ThisWorkbook.ActiveSheet.Range("Variable_Range"))
    For Each i In Rng
       'perform an action on I where I is randomly selected.
    Next i
End Sub

Я предпочитаю рандомизировать порядок, а не просто случайным образом выбирать ячейку, где ячейка может быть выбрана более одного раза.

Заранее спасибо.


person RRP    schedule 23.11.2020    source источник
comment
Я не уверен, что понял вопрос с точки зрения выбора ячейки более одного раза. Разрешат или нет?   -  person FaneDuru    schedule 23.11.2020


Ответы (3)


Вот возможное решение. Я добавляю все ячейки в соответствующем диапазоне в коллекцию. Затем я перемещаюсь по коллекции, используя случайные индексы. После посещения индекса я удаляю его из коллекции и повторяю процесс.

Это работает для вас?

Изменить: нет необходимости вызывать метод c.Count для каждой итерации. Мы сами можем справиться с этим. Вероятно, это будет немного эффективнее, чем вызов метода объекта.

Sub SuperTester()
    Dim c As Collection
    Dim rng As Range
    Dim cel As Range
    Dim idx As Long
    Dim remainingCount As Long
    
    Set rng = Range("A2:A17")
    Set c = New Collection
    
    For Each cel In rng
        c.Add cel
    Next cel
    
    remainingCount = c.Count
    While remainingCount > 0
        idx = WorksheetFunction.RandBetween(1, c.Count)
        Debug.Print c.Item(idx).Address
        c.Remove idx
        
        remainingCount = remainingCount - 1
    Wend
    
End Sub
person basodre    schedule 23.11.2020
comment
@SiddharthRout Спасибо! - person basodre; 23.11.2020
comment
где добавить звонок на сотовый? Прямо перед строкой удаления? Например, скажем, я просто хотел выбрать ячейку... это правильное место? c.select c.Удалить idx - person RRP; 23.11.2020
comment
@RRP В моей строке Debug.Print... я действую в выбранной ячейке. Вы можете удалить строку и заменить ее тем, что вы хотите сделать с ней. Моим предпочтительным решением было бы создание отдельной процедуры, которая принимает объект Range в качестве параметра. Это разделение облегчает изменение вашего ответа без изменения основной функции. Что-то вроде: Sub PerformActions(rng as Range) .... Затем вы можете заменить Debug... на PerformActions(c.Item(idx)) - person basodre; 23.11.2020

Вы можете использовать WorksheetFunction.RandBetween, чтобы получить случайное число между двумя числами. Однако числа не будут уникальными. Если вы хотите уникальности, вам придется использовать немного другой подход.

Option Explicit

Sub Solver_Step_Evo()
    Dim Rng As Range
    
    Set Rng = GetRange(ThisWorkbook.ActiveSheet.Range("Variable_Range"))
    
    Dim lowerBound As Long: lowerBound = 1
    Dim UpperBound As Long: UpperBound = Rng.Cells.Count
    
    Dim randomI As Long
    Dim i As Long
    
    For i = lowerBound To UpperBound
        randomI = Application.WorksheetFunction.RandBetween(lowerBound, UpperBound)
        Debug.Print randomI
    Next i
End Sub
person Siddharth Rout    schedule 23.11.2020

Попробуйте следующую функцию, пожалуйста:

Function RndCell(rng As Range) As Range
 Dim rndRow As Long, rndCol As Long
 
 rndRow = WorksheetFunction.RandBetween(1, rng.rows.count)
 rndCol = WorksheetFunction.RandBetween(1, rng.Columns.count)
 Set RndCell = rng.cells(rndRow, rndCol)
End Function

Это можно проверить с помощью следующего простого подпрограммы:

Sub testSelectRandomCell()
  Dim rng As Range
  Set rng = Range("A2:D10")
  RndCell(rng).Select
End Sub

Отредактировано:

Если случайно выбранные ячейки не должны повторяться, функцию можно адаптировать следующим образом (используя массив Static для сохранения уже выбранных ячеек):

Function RndCellOnce(rng As Range, Optional boolClear As Boolean = False) As Range
 Dim rndRow As Long, rndCol As Long, k As Long, El, arr1
 Static arr
 
 If boolClear And IsArray(arr) Then Erase arr
DoItAgain:
    rndRow = WorksheetFunction.RandBetween(1, rng.rows.count)
    rndCol = WorksheetFunction.RandBetween(1, rng.Columns.count)
 If IsArray(arr) Then
    If UBound(arr) = rng.cells.count - 1 Then
        rng.Interior.Color = xlNone
        ReDim arr(0): GoTo Over
    End If
    For Each El In arr
        If El <> "" Then
            arr1 = Split(El, "|")
            If CLng(arr1(0)) = rndRow And CLng(arr1(1)) = rndCol Then GoTo DoItAgain
        End If
    Next El
    ReDim Preserve arr(UBound(arr) + 1)
Else
    ReDim arr(0)
End If
Over:
arr(UBound(arr)) = rndRow & "|" & rndCol
 Set RndCellOnce = rng.cells(rndRow, rndCol)
End Function

Его можно протестировать со следующим Sub. Для визуальной проверки каждая выбранная ячейка будет окрашена в желтый цвет. Когда все ячейки диапазона будут выбраны (одна за другой), статический массив будет стерт, а внутренний цвет будет очищен:

Sub testSelectRandomCell()
  Dim rng As Range

  Set rng = Range("A2:D10")
  With RndCellOnce(rng)
    .Interior.Color = vbYellow
    .Select
  End With
End Sub
person FaneDuru    schedule 23.11.2020