programing

VB.NET의 임의 정수

nasanasas 2020. 12. 28. 08:15
반응형

VB.NET의 임의 정수


단위 테스트에 사용할 1과 n (n은 양의 정수) 사이의 임의의 정수를 생성해야합니다. 진정한 임의성을 보장하기 위해 지나치게 복잡한 것은 필요하지 않습니다. 단지 구식 난수 일뿐입니다.

어떻게할까요?


1과 N (포함) 사이의 임의의 정수 값을 얻으려면 다음을 사용할 수 있습니다.

CInt(Math.Ceiling(Rnd() * n)) + 1

여러 번 지적했듯이 다음과 같은 코드를 작성하라는 제안은 문제가 있습니다.

Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
    Dim Generator As System.Random = New System.Random()
    Return Generator.Next(Min, Max)
End Function

그 이유는 Random클래스 생성자 가 시스템 시계를 기반으로 기본 시드를 제공 하기 때문입니다 . 대부분의 시스템에서 이는 20ms 부근의 제한된 세분성을 갖습니다. 따라서 다음 코드를 작성하면 동일한 번호를 연속으로 여러 번 얻을 수 있습니다.

Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
    randoms(i) = GetRandom(1, 100)
Next

다음 코드는이 문제를 해결합니다.

Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
    ' by making Generator static, we preserve the same instance '
    ' (i.e., do not create new instances with the same seed over and over) '
    ' between calls '
    Static Generator As System.Random = New System.Random()
    Return Generator.Next(Min, Max)
End Function

두 가지 방법을 모두 사용하여 1에서 100 사이의 임의의 정수 25 개를 생성하는 간단한 프로그램을 작성했습니다. 출력은 다음과 같습니다.

Non-static: 70 Static: 70
Non-static: 70 Static: 46
Non-static: 70 Static: 58
Non-static: 70 Static: 19
Non-static: 70 Static: 79
Non-static: 70 Static: 24
Non-static: 70 Static: 14
Non-static: 70 Static: 46
Non-static: 70 Static: 82
Non-static: 70 Static: 31
Non-static: 70 Static: 25
Non-static: 70 Static: 8
Non-static: 70 Static: 76
Non-static: 70 Static: 74
Non-static: 70 Static: 84
Non-static: 70 Static: 39
Non-static: 70 Static: 30
Non-static: 70 Static: 55
Non-static: 70 Static: 49
Non-static: 70 Static: 21
Non-static: 70 Static: 99
Non-static: 70 Static: 15
Non-static: 70 Static: 83
Non-static: 70 Static: 26
Non-static: 70 Static: 16
Non-static: 70 Static: 75

System.Random 사용 :

Dim MyMin As Integer = 1, MyMax As Integer = 5, My1stRandomNumber As Integer, My2ndRandomNumber As Integer

' Create a random number generator
Dim Generator As System.Random = New System.Random()

' Get a random number >= MyMin and <= MyMax
My1stRandomNumber = Generator.Next(MyMin, MyMax + 1) ' Note: Next function returns numbers _less than_ max, so pass in max + 1 to include max as a possible value

' Get another random number (don't create a new generator, use the same one)
My2ndRandomNumber = Generator.Next(MyMin, MyMax + 1)

지금까지의 모든 답변에는 문제 나 버그가 있습니다 (하나가 아닌 복수). 설명을하겠습니다. 그러나 먼저 저는 Generator 변수를 기억하기 위해 정적 변수를 사용하는 Dan Tao의 통찰력을 칭찬하고 싶습니다. 그래서 여러 번 호출하면 동일한 #이 반복되지 않고 아주 좋은 설명을했습니다. 그러나 그의 코드는 내가 지금 설명하는 것처럼 대부분의 다른 사람들과 동일한 결함을 겪었습니다.

MS는 Next () 메서드를 다소 이상하게 만들었습니다. Min 매개 변수는 예상대로 포함되는 최소값이지만 Max 매개 변수는 예상하지 못한 배타적 최대 값입니다. 즉, min = 1 및 max = 5를 전달하면 난수는 1, 2, 3 또는 4 중 하나가되지만 5는 포함되지 않습니다. Microsoft의 Random.Next () 메서드를 사용합니다.

간단한 답변 (하지만 여전히 다른 가능성이 있지만 드문 문제가 있음)을 보려면 다음을 사용해야합니다.

Private Function GenRandomInt(min As Int32, max As Int32) As Int32
    Static staticRandomGenerator As New System.Random
    Return staticRandomGenerator.Next(min, max + 1)
End Function

(나는 int가 얼마나 큰지 더 명확하게 Int32하기보다 사용 하는 것을 좋아하고 Integer, 입력하는 것이 더 짧지 만 자신에게 적합합니다.)

이 방법에는 두 가지 잠재적 인 문제가 있지만 대부분의 용도에 적합하고 정확합니다. 따라서 간단한 해결책 을 원한다면 이것이 맞다고 생각합니다.

이 함수에서 볼 수있는 유일한 두 가지 문제는 1 : Max = Int32.MaxValue 일 때 1을 더하면 숫자 오버플로가 발생합니다. 알토, 이것은 드문 일이지만 여전히 가능성이 있습니다. 2 : min> max + 1. 일 때 min = 10이고 max = 5이면 Next 함수는 오류를 던집니다. 이것은 당신이 원하는 것일 수 있습니다. 그러나 둘 다 아닐 수도 있습니다. 또는 1을 추가하여 min = 5 및 max = 4. 일 때 고려하십시오 .Next 메서드에 5가 전달되지만 실제로 오류 인 경우 오류가 발생하지 않지만 테스트 한 Microsoft .NET 코드는 5를 반환하므로 최대 = 최소 일 때 실제로 '배타적'최대가 아닙니다. 그러나 Random.Next () 함수에 대해 max <min이면 ArgumentOutOfRangeException이 발생합니다. 따라서 Microsoft의 구현은 이와 관련하여 매우 일관성이없고 버그가 많습니다.

min> max 일 때 단순히 숫자를 바꾸어 오류가 발생하지 않도록 할 수 있지만 전적으로 원하는 것에 달려 있습니다. 유효하지 않은 값에 대한 오류를 원하는 경우 코드에서 Microsoft의 배타적 최대 값 (최대 + 1)이 최소값과 같을 때 오류를 던지는 것이 좋습니다. 여기서 MS는이 경우 오류가 발생하지 않습니다.

max = Int32.MaxValue 때에 대한 해결 방법을 처리하는 것이 약간 불편하지만 이러한 두 상황을 모두 처리하는 철저한 함수를 게시 할 예정입니다. 내가 코딩 한 것과 다른 행동을 원한다면 자신에게 적합합니다. 그러나이 두 가지 문제에 유의하십시오.

즐거운 코딩 되세요!

편집 : 그래서 나는 임의의 정수 생성기가 필요했고 그것을 '올바르게'코딩하기로 결정했습니다. 따라서 누구나 전체 기능을 원하는 경우 실제로 작동하는 기능이 있습니다. (하지만 2 줄의 코드만으로 가장 단순한 상을받지는 않습니다.하지만 그렇게 복잡하지도 않습니다.)

''' <summary>
''' Generates a random Integer with any (inclusive) minimum or (inclusive) maximum values, with full range of Int32 values.
''' </summary>
''' <param name="inMin">Inclusive Minimum value. Lowest possible return value.</param>
''' <param name="inMax">Inclusive Maximum value. Highest possible return value.</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function GenRandomInt(inMin As Int32, inMax As Int32) As Int32
    Static staticRandomGenerator As New System.Random
    If inMin > inMax Then Dim t = inMin : inMin = inMax : inMax = t
    If inMax < Int32.MaxValue Then Return staticRandomGenerator.Next(inMin, inMax + 1)
    ' now max = Int32.MaxValue, so we need to work around Microsoft's quirk of an exclusive max parameter.
    If inMin > Int32.MinValue Then Return staticRandomGenerator.Next(inMin - 1, inMax) + 1 ' okay, this was the easy one.
    ' now min and max give full range of integer, but Random.Next() does not give us an option for the full range of integer.
    ' so we need to use Random.NextBytes() to give us 4 random bytes, then convert that to our random int.
    Dim bytes(3) As Byte ' 4 bytes, 0 to 3
    staticRandomGenerator.NextBytes(bytes) ' 4 random bytes
    Return BitConverter.ToInt32(bytes, 0) ' return bytes converted to a random Int32
End Function

Public Function RandomNumber(ByVal n As Integer) As Integer
    'initialize random number generator
    Dim r As New Random(System.DateTime.Now.Millisecond)
    Return r.Next(1, n)
End Function

Microsoft 예제 Rnd 함수

https://msdn.microsoft.com/en-us/library/f7s023d2%28v=vs.90%29.aspx

1- 난수 생성기를 초기화합니다.

Randomize()

2-1과 6 사이의 임의 값을 생성합니다.

Dim value As Integer = CInt(Int((6 * Rnd()) + 1))

훌륭한 대답 인 Joseph의 대답을 사용하고 있으며 다음과 같이 연속적으로 실행합니다.

dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715)

그러면 호출을 너무 빨리 처리하기 때문에 결과가 계속해서 동일하게 돌아올 수 있습니다. 이것은 '08 년에는 문제가되지 않았을 수 있지만, 오늘날 프로세서가 훨씬 빠르기 때문에이 기능은 두 번째 호출을하기 전에 시스템 시계를 변경할 수있는 충분한 시간을 허용하지 않습니다.

System.Random () 함수는 시스템 시계를 기반으로하기 때문에 다음 호출 전에 변경 될 수 있도록 충분한 시간을 허용해야합니다. 이를 수행하는 한 가지 방법은 현재 스레드를 1 밀리 초 동안 일시 중지하는 것입니다. 아래 예를 참조하십시오.

Public Function GetRandom(ByVal min as Integer, ByVal max as Integer) as Integer
    Static staticRandomGenerator As New System.Random
    max += 1
    Return staticRandomGenerator.Next(If(min > max, max, min), If(min > max, min, max))
End Function

의사 난수 생성기는 한 번만 생성해야합니다.

Dim Generator As System.Random = New System.Random()

그런 다음 필요에 따라 정수로 충분하면 다음을 사용할 수 있습니다.

Public Function GetRandom(myGenerator As System.Random, ByVal Min As Integer, ByVal Max As Integer) As Integer
'min is inclusive, max is exclusive (dah!)
Return myGenerator.Next(Min, Max + 1)
End Function

원하는만큼 여러 번. 래퍼 함수를 ​​사용하는 것은 최대 값이 배타적이기 때문에 정당화됩니다. 난수는 이런 식으로 작동하지만 .Next의 정의는 혼란 스럽습니다.

제 생각에는 숫자가 필요할 때마다 생성기를 만드는 것은 잘못된 것입니다. 의사 난수는 이런 식으로 작동하지 않습니다.

첫째, 다른 답변에서 논의 된 초기화 문제가 발생합니다. 한 번 초기화하면이 문제가 없습니다.

둘째, 난 당신이 유효한 난수 시퀀스를 얻는 지 전혀 확신하지 못합니다. 오히려 컴퓨터 시간에 따라 자동으로 시드되는 여러 다른 시퀀스의 첫 번째 컬렉션을 가져옵니다. 이 숫자가 시퀀스의 무작위성을 확인하는 테스트를 통과할지 확실하지 않습니다.


Dim rnd As Random = New Random
rnd.Next(n)

참고로 RND 및 RANDOMIZE에 대한 VB NET Fuction 정의 (BASIC (1980 년) 및 이후 모든 버전의 동일한 결과를 제공해야 함)는 다음과 같습니다.

Public NotInheritable Class VBMath
    ' Methods
    Private Shared Function GetTimer() As Single
        Dim now As DateTime = DateTime.Now
        Return CSng((((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (CDbl(now.Millisecond) / 1000)))
    End Function

    Public Shared Sub Randomize()
        Dim timer As Single = VBMath.GetTimer
        Dim projectData As ProjectData = ProjectData.GetProjectData
        Dim rndSeed As Integer = projectData.m_rndSeed
        Dim num3 As Integer = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0)
        num3 = (((num3 And &HFFFF) Xor (num3 >> &H10)) << 8)
        rndSeed = ((rndSeed And -16776961) Or num3)
        projectData.m_rndSeed = rndSeed
    End Sub

    Public Shared Sub Randomize(ByVal Number As Double)
        Dim num2 As Integer
        Dim projectData As ProjectData = ProjectData.GetProjectData
        Dim rndSeed As Integer = projectData.m_rndSeed
        If BitConverter.IsLittleEndian Then
            num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4)
        Else
            num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0)
        End If
        num2 = (((num2 And &HFFFF) Xor (num2 >> &H10)) << 8)
        rndSeed = ((rndSeed And -16776961) Or num2)
        projectData.m_rndSeed = rndSeed
    End Sub

    Public Shared Function Rnd() As Single
        Return VBMath.Rnd(1!)
    End Function

    Public Shared Function Rnd(ByVal Number As Single) As Single
        Dim projectData As ProjectData = ProjectData.GetProjectData
        Dim rndSeed As Integer = projectData.m_rndSeed
        If (Number <> 0) Then
            If (Number < 0) Then
                Dim num1 As UInt64 = (BitConverter.ToInt32(BitConverter.GetBytes(Number), 0) And &HFFFFFFFF)
                rndSeed = CInt(((num1 + (num1 >> &H18)) And CULng(&HFFFFFF)))
            End If
            rndSeed = CInt((((rndSeed * &H43FD43FD) + &HC39EC3) And &HFFFFFF))
        End If
        projectData.m_rndSeed = rndSeed
        Return (CSng(rndSeed) / 1.677722E+07!)
    End Function

End Class

Random CLASS는 다음과 같습니다.

Public Class Random
    ' Methods
    <__DynamicallyInvokable> _
    Public Sub New()
        Me.New(Environment.TickCount)
    End Sub

    <__DynamicallyInvokable> _
    Public Sub New(ByVal Seed As Integer)
        Me.SeedArray = New Integer(&H38  - 1) {}
        Dim num4 As Integer = If((Seed = -2147483648), &H7FFFFFFF, Math.Abs(Seed))
        Dim num2 As Integer = (&H9A4EC86 - num4)
        Me.SeedArray(&H37) = num2
        Dim num3 As Integer = 1
        Dim i As Integer
        For i = 1 To &H37 - 1
            Dim index As Integer = ((&H15 * i) Mod &H37)
            Me.SeedArray(index) = num3
            num3 = (num2 - num3)
            If (num3 < 0) Then
                num3 = (num3 + &H7FFFFFFF)
            End If
            num2 = Me.SeedArray(index)
        Next i
        Dim j As Integer
        For j = 1 To 5 - 1
            Dim k As Integer
            For k = 1 To &H38 - 1
                Me.SeedArray(k) = (Me.SeedArray(k) - Me.SeedArray((1 + ((k + 30) Mod &H37))))
                If (Me.SeedArray(k) < 0) Then
                    Me.SeedArray(k) = (Me.SeedArray(k) + &H7FFFFFFF)
                End If
            Next k
        Next j
        Me.inext = 0
        Me.inextp = &H15
        Seed = 1
    End Sub

    Private Function GetSampleForLargeRange() As Double
        Dim num As Integer = Me.InternalSample
        If ((Me.InternalSample Mod 2) = 0) Then
            num = -num
        End If
        Dim num2 As Double = num
        num2 = (num2 + 2147483646)
        Return (num2 / 4294967293)
    End Function

    Private Function InternalSample() As Integer
        Dim inext As Integer = Me.inext
        Dim inextp As Integer = Me.inextp
        If (++inext >= &H38) Then
            inext = 1
        End If
        If (++inextp >= &H38) Then
            inextp = 1
        End If
        Dim num As Integer = (Me.SeedArray(inext) - Me.SeedArray(inextp))
        If (num = &H7FFFFFFF) Then
            num -= 1
        End If
        If (num < 0) Then
            num = (num + &H7FFFFFFF)
        End If
        Me.SeedArray(inext) = num
        Me.inext = inext
        Me.inextp = inextp
        Return num
    End Function

    <__DynamicallyInvokable> _
    Public Overridable Function [Next]() As Integer
        Return Me.InternalSample
    End Function

    <__DynamicallyInvokable> _
    Public Overridable Function [Next](ByVal maxValue As Integer) As Integer
        If (maxValue < 0) Then
            Dim values As Object() = New Object() { "maxValue" }
            Throw New ArgumentOutOfRangeException("maxValue", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", values))
        End If
        Return CInt((Me.Sample * maxValue))
    End Function

    <__DynamicallyInvokable> _
    Public Overridable Function [Next](ByVal minValue As Integer, ByVal maxValue As Integer) As Integer
        If (minValue > maxValue) Then
            Dim values As Object() = New Object() { "minValue", "maxValue" }
            Throw New ArgumentOutOfRangeException("minValue", Environment.GetResourceString("Argument_MinMaxValue", values))
        End If
        Dim num As Long = (maxValue - minValue)
        If (num <= &H7FFFFFFF) Then
            Return (CInt((Me.Sample * num)) + minValue)
        End If
        Return (CInt(CLng((Me.GetSampleForLargeRange * num))) + minValue)
    End Function

    <__DynamicallyInvokable> _
    Public Overridable Sub NextBytes(ByVal buffer As Byte())
        If (buffer Is Nothing) Then
            Throw New ArgumentNullException("buffer")
        End If
        Dim i As Integer
        For i = 0 To buffer.Length - 1
            buffer(i) = CByte((Me.InternalSample Mod &H100))
        Next i
    End Sub

    <__DynamicallyInvokable> _
    Public Overridable Function NextDouble() As Double
        Return Me.Sample
    End Function

    <__DynamicallyInvokable> _
    Protected Overridable Function Sample() As Double
        Return (Me.InternalSample * 4.6566128752457969E-10)
    End Function


    ' Fields
    Private inext As Integer
    Private inextp As Integer
    Private Const MBIG As Integer = &H7FFFFFFF
    Private Const MSEED As Integer = &H9A4EC86
    Private Const MZ As Integer = 0
    Private SeedArray As Integer()
End Class

Function xrand() As Long
        Dim r1 As Long = Now.Day & Now.Month & Now.Year & Now.Hour & Now.Minute & Now.Second & Now.Millisecond
        Dim RAND As Long = Math.Max(r1, r1 * 2)
        Return RAND
End Function

[BBOYSE] 처음부터이게 최선의 방법입니다 : P

ReferenceURL : https://stackoverflow.com/questions/18676/random-integer-in-vb-net

반응형