Infinite opportunities?  Endless problems?  Limited resources?  Talk to us!
Operations
Consulting
Software
Consulting
Publishing and
Training
Applied
Solutions
Excel add-ins
and tutorials
Charts Excel and VBA
Case Studies
VBA
tutorials
PowerPoint
Add-Ins
Miscellenia
Selecting a random subset without repeating -- using a user defined function (UDF)

There are three procedures described below.  Each is a variant of an efficient single-pass algorithm for generating a random selection from a specified list.

A worksheet range contains the list from which the random selections are made.

Return a specified number of random numbers from a set of numbers with a specified lower and upper values

Return a specified number of random values from an user specified array

A worksheet range contains the list from which the random selections are made.

The code below goes into a normal module.  The function RandomSelection is then available for use in a spreadsheet.  Typically, the function should be used as an array-formula.  The function takes a single argument, a worksheet range from the random selections are to be made.  How many selections depends on the number of cells selected for array-entering the function.

Option Explicit
Function RandomSelection(aRng As Range)
    Dim myTarg As Range, _
        SrcList, Rslt(), _
        i As Long, j As Long, k As Long
    Application.Volatile
    SrcList = aRng.Value
    Set myTarg = Application.Caller
    With myTarg
    If .Areas.Count > 1 Then
         RandomSelection = _
             "Function can be used only in a single contiguous range"
        Exit Function   '<<<<<
        End If
    If .Rows.Count > 1 And .Columns.Count > 1 Then
        RandomSelection = _
            "Selected cells must be in a single row or column"
        Exit Function   '<<<<<
        End If
    If .Cells.Count > aRng.Cells.Count Then
        RandomSelection = _
            "Range specified as argument must contain more cells than output selection"
        Exit Function   '<<<<<
        End If
    ReDim Rslt(1 To IIf(.Rows.Count > 1, .Rows.Count, .Columns.Count))
        End With
    j = UBound(SrcList, 1)
    For i = LBound(Rslt) To UBound(Rslt)
        k = Int(Rnd() * (j - LBound(SrcList, 1) + 1)) + LBound(SrcList, 1)
        Rslt(i) = SrcList(k, 1)
        SrcList(k, 1) = SrcList(j, 1)
        j = j - 1
        Next i
    If myTarg.Rows.Count > 1 Then
        RandomSelection = Application.WorksheetFunction.Transpose(Rslt)
    Else
        RandomSelection = Rslt
        End If
    End Function

Return a specified number of random numbers from a set of numbers with a specified lower and upper values

This is a variant of a single pass algorithm that generates Amount random numbers from a list of numbers with the lowest number specified by Bottom and the highest number specified by TopWhile this can be used as a UDF, the more likely use will be to call it from another VBA procedure.  As a UDF, it would be used as in =TMOptRands(11, 20,5), which returns 5 random numbers from the set (11, 12,...,19, 20).

Public Function TMOptRands(Bottom As Long, Top As Long, _
         Amount As Long) As Variant
    Dim i As Long, r As Long, temp As Long
   
    ReDim iArr(Bottom To Top) As Long
    For i = Bottom To Top: iArr(i) = i: Next i
    For i = 1 To Amount
        r = Int(Rnd() * (Top - Bottom + 1 - (i - 1))) _
            + (Bottom + (i - 1))
        temp = iArr(r): iArr(r) = iArr(Bottom + i - 1): _
            iArr(Bottom + i - 1) = temp
        Next i
    ReDim Preserve iArr(Bottom To Bottom + Amount - 1)
    TMOptRands = iArr
    End Function

Return a specified number of random values from an user specified array

This is a variant of a single pass algorithm that returns N random elements from the contents of Arr.  The same array is used to return the numbers and can be reused to create another list.  The N random numbers are returned in the upper N array elements.  While this can be used as a UDF, the more likely use will be to call it from another VBA procedure.  One way to use this function as a UDF would be =RandomSelect({101, 34, 55, 12.},2).  The example returns 2 random numbers from 101, 34, 55, 12.

Sub Swap(ByRef Arr() As Variant, ByVal i As Long, ByVal j As Long)
        Dim temp As Variant
        temp = Arr(i): Arr(i) = Arr(j): Arr(j) = temp
        End Sub
Sub RandomSelect(ByRef Arr() As Variant, ByVal N As Long)
        'Returns N elements out of m, the size of Arr _
         The upper N elements of the array will contain the _
         unique random values
    Dim i As Long, thisIdx As Long
    'Need edits to ensure Arr is an acceptable data type. _
     Similarly, validate n
    For i = 1 To N
        thisIdx = LBound(Arr) _
            + Int((UBound(Arr) - (i - 1) - LBound(Arr) + 1) * Rnd())
        Swap Arr, UBound(Arr) - (i - 1), thisIdx
        Next i
    End Sub

 

For custom technology solutions, operations consulting, or training contact web-underscore-contact@tushar-hyphen-mehta-dot-cee-oh-em.
By accessing any page or link on this web site other than this page, you agree to the terms and conditions.

Ads from amazon.com

[Optional] Survey (current rating of site: 3.7 out of 4)  If you will take a moment to provide your comments, it will help improve the site both for you, and for other visitors.

On a scale of 4 (just what I need)
to zero (totally useless)

How do you rate the information

on this page?

  on this site?

[Optional]

Your name
Your email address
Other comments
 
Monitor page
for changes
    
   it's private  

by ChangeDetection
A comment selected at random:

 

 

Copyright © 2000-2008 Tushar Mehta.
Send comments and suggestions about the web site to webmaster@tushar-hyphen-mehta-dot-cee-oh-em
Last edited April 13, 2008