0

コードのバグがどこにあるのか、または何かわからないことがあるのか​​わからないため、すべてのオプションを使い果たしました。助けを切望しています。

「イタチだと思う!」を作ろうとしています。リチャード・ドーキンスの 80 年代後半の進化に関するドキュメンタリーのミミック。目標は、アルゴリズムが突然変異とフィットネス トーナメントを通じて正しい答えを推測するまで、遺伝的アルゴリズムを進めることです。

さて、問題は次のとおりです。

    Private Function fitnessTourney(ByVal editGuess() As Guess, ByVal popIndex As Integer, ByVal tourneySize As Integer, ByVal popNum As Integer)
    Dim randInt(tourneySize - 1) As Integer
    Dim loopCount1 As Integer = 0
    Dim fitnessWinner As New Guess
    fitnessWinner.setFitness(-50)

...そして、このループで重大なエラーが発生しています...

    For i = 0 To tourneySize - 1
        Randomize()
        randInt(i) = Int(Rnd() * popNum)
        While editGuess(randInt(i)).Used = True
            If loopCount1 > tourneySize Then
                loopCount1 = 0
                For i2 = 0 To popNum - 1
                    editGuess(i2).setUsed(False)
                Next
                i = -1
                Continue For
            End If
            loopCount1 += 1
            randInt(i) = Int(Rnd() * popNum)
        End While
        editGuess(randInt(i)).determineFitness(correctPhrase)
        editGuess(randInt(i)).setUsed(True)
    Next
    For i = 0 To popNum - 1
        editGuess(i).setUsed(False)
    Next

このループが行おうとしているのは、オブジェクトの editGuess 配列の 4 つのランダムなインスタンスを選択することです。このループは、母集団が 10 人のメンバーのうちの 1 人と競合しているため、1 つが複数回使用されるのを防ごうとします (選択された 4 人の候補者の中で最も高い適合度が勝つはずです)。

重大なエラーは、editGuess(randInt(i)).Used のインスタンスが常に true と評価されるエンドレス ループが不思議なことに発生することです。ループが多すぎる場合は、すべてのインスタンスを False にリセットすることで、これを修正しようとしました。

問題は、デバッガーですべてのインスタンスを False と評価することです。次に、「editGuess(randInt(i)).setUsed(True)」(「editGuess(randInt(i)).Used = True」とまったく同じ)に達すると、配列のすべてのメンバーにこの値が設定されます.

何が起こっているかを見ることができる人はいますか?私はこれを完了するのにとても近いです!

ゲスクラスは次のとおりです。

    Public Class Guess
Dim Fitness As Integer
Dim strLength As Integer
Dim strArray(30) As String
Dim guessStr As String
Dim Used As Boolean
Public Sub New()
    Fitness = 0
    guessStr = ""
    strLength = 0
    Used = 0
End Sub
Public Sub determineFitness(ByVal correctPhrase As String)
    Dim lowerVal
    If guessStr.Length <= correctPhrase.Length Then
        lowerVal = guessStr.Length
    Else
        lowerVal = correctPhrase.Length
    End If
    strArray = guessStr.Split("")
    Fitness = 0 - Math.Abs(correctPhrase.Length - guessStr.Length)
    For i = 0 To lowerVal - 1
        If correctPhrase(i) = guessStr(i) Then
            Fitness = Fitness + 1
        End If
    Next
End Sub
Public Sub Mutate(ByVal mutatepercentage As Decimal, ByVal goodLetters As String)
    If mutatepercentage > 100 Then
        mutatepercentage = 100
    End If
    If mutatepercentage < 0 Then
        mutatepercentage = 0
    End If
    mutatepercentage = mutatepercentage / 100
    If Rnd() < mutatepercentage Then
        strLength = Int(Rnd() * 25) + 5
        If strLength < guessStr.Length Then
            guessStr = guessStr.Remove(strLength - 1)
        End If
    End If
    For i = 0 To strLength - 1
        If Rnd() < mutatepercentage Then
            If i < guessStr.Length Then
                guessStr = guessStr.Remove(i, 1).Insert(i, goodLetters(Int(Rnd() * goodLetters.Length)))
            Else
                guessStr = guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
            End If
        End If
    Next
End Sub
Public Sub setFitness(ByVal num As Integer)
    Fitness = num
End Sub
Public Sub setStrLength(ByVal num As Integer)
    strLength = num
End Sub
Public Sub initializeText()

End Sub
Public Sub setUsed(ByVal bVal As Boolean)
    Used = bVal
End Sub

クラス終了

最後に、関数が呼び出される場所と方法は次のとおりです。

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    population1(counter) = fitnessTourney(population1, counter, 4, 10)
    population2(counter) = fitnessTourney(population2, counter, 4, 10)
    population1(counter).Mutate(2, goodLetters)
    population2(counter).Mutate(20, goodLetters)
    Label1.Text = population1(counter).guessStr
    Label2.Text = population2(counter).guessStr
    counter += 1
    If counter > 9 Then
        counter = 0
    End If
End Sub

クラス終了

EDIT 1: コメントありがとうございます。

フォームに使用するカスタム コンストラクターを次に示します。これは、editGuess を使用してfitnessTourney 関数に渡される人口配列を設定するために使用されます。

Public Sub New()
    InitializeComponent()
    Randomize()
    For i = 0 To 9
        population1(i) = New Guess
        population2(i) = New Guess
    Next
    counter = 0
    correctPhrase = "Methinks it is a weasel!"
    goodLetters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ !@#$%^&*()_+-=?></.,;\|`'~"
    goodLettersArr = goodLetters.Split("")
    For i = 0 To 9
        population1(i).setStrLength(Int(Rnd() * 25) + 5)
        population2(i).setStrLength(Int(Rnd() * 25) + 5)
        For i2 = 0 To population1(i).strLength
            population1(i).guessStr = population1(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
        Next
        For i2 = 0 To population2(i).strLength
            population2(i).guessStr = population2(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
        Next
        Label1.Text = population1(i).guessStr
        Label2.Text = population2(i).guessStr
    Next
    population1(0).guessStr = correctPhrase
    population1(0).determineFitness(correctPhrase)
End Sub
4

1 に答える 1

0

すべてのコードを徹底的に調べたわけではありませんが、大きな問題の 1 つはRandomize、ループ内から呼び出していることです。を呼び出すたびRandomizeに、乱数に現在の時刻が再シードされます。したがって、時計が変わる前に複数回呼び出すと、その時間を使用してシーケンスの最初の「乱数」を取得し続け、常に同じ数値に評価されます。「乱数」を生成する場合、乱数ジェネレーターを再シードする回数はできるだけ少なくします。できれば、アプリケーションの起動時に一度だけシードすることをお勧めします。

Randomize補足として、古い VB6 スタイルとRndメソッドを使用しないでください。これらは下位互換性のために VB.NET でのみ提供されます。代わりにRandomクラスを使用する必要があります。使い方も簡単です。クラスを使用Randomすると、オブジェクトをインスタンス化した時点で自動的にシードされるため、randomize のようなメソッドを呼び出す必要さえありません。したがって、Randomクラスの場合、オブジェクトを使用する可能性のあるループに入る前に、オブジェクトを一度だけインスタンス化するように注意する必要があります。Randomループ内で新しいオブジェクトを作成すると、同様に同じ数が生成され続けます。

于 2013-02-19T13:21:42.353 に答える