質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

pythonのリスト置換におけるバグの質問

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 667

go0x

score 11

 遺伝的アルゴリズムの設計(超初心者)

[注意]プログラミングをはじめて1週間ほどの初心者です。

遺伝的アルゴリズムと書いていますが全く厳密なものではなく、内容は適当です。

[概要]言語:Python
1, 1~9の数値を20個持つリストをランダムに10体生成
2, 0を最も多く持つリスト(father)、2番目に多く持つリスト(mother)を判定
3, fatherとmotherの持つ要素をインデックス毎に集約しリスト化(samples)
4, samplesからインデックス毎にランダムに要素を取り出し新世代(newgene)を10体生成
5, 手順2~4をループ

ここまでの動作は問題なく機能します。

 発生している問題

何周かループすると全て同じリストになり(0の数が全て同じになる)、変化が止まるので、
その場合(newgeneが全て同率1位)のみ突然変異として、fatherの1箇所を
ランダムな数値に変更(リストの置換)する処理をしています。
この時、fatherのみ変更しているにも関わらず、なぜかmotherにも全く同じ変更が発生します。
そのため突然変異が実現できていません。

 該当のソースコード

import random

# 0~9の数値をランダムに20個持つリスト kid を10個作成し kids にリスト化

kid = []
kids = []
for _ in range(11):
    kids.append(kid)
    kid = []
    for _ in range(20):
        kid.append(random.randint(0, 9))
kids.pop(0)

loop = 1

# --------------------以下ループ-------------------------
# だいたい1000回以内に同率1位の状態となり、58行目がうまくいかない問題が発生する

for _ in range(1000):

    # kids の 0 の数をカウントし each_0_counts にリスト化

    each_0_counts = []
    for x in kids:
        each_0_counts.append(x.count(0))

    # 最も多い 0 の個数を max_score
    # その次に多い 0 の個数を premax_score とする

    max_score =  max(each_0_counts)
    premax_score = 0
    for x in each_0_counts:
        if x == max_score:
            continue
        elif x > premax_score:
            premax_score = x
        else:
            pass

    # 0の個数が全て同じ場合(同率1位)の処理

    if premax_score == 0:
        premax_score = max_score
    else:
        pass

    # 1位、2位のリスト番号を取得し、fatherとmotherを決める

    father_index = each_0_counts.index(max_score)
    mother_index = each_0_counts.index(premax_score)

    father = kids[father_index]
    mother = kids[mother_index]

    # 問題の箇所
    # 同率1位の場合、変化の停止を避けるため、fatherの一部をランダムに変更する
    # このときfather、motherをプリントして調べてみると、なぜかmotherまで変更される

    if premax_score == max_score:
        father[random.randint(0, 19)] = random.randint(0, 9)
    else:
        pass

    parents = []
    parents.append(father)
    parents.append(mother)

    print("\n父 : " + str(father))
    print("母 : " + str(mother))

    # parentsの要素をインデックス毎にsamplesにリスト化

    sample = []
    samples = []
    for y in range(21):
        samples.append(sample)
        sample = []
        for x in parents:
                try:
                    sample.append(x[y])
                except IndexError:
                    continue
    samples.pop(0)

    # sampleからランダムに要素を追加し、新しい個体(newgenes)を10体生成

    newgenes =  []
    for _ in range(10):
        newgenes.append([])

    xx = -1
    xy = 0
    for _ in range(20):
        xx += 1
        xy = 0
        for _ in range(10):
            try:
                add = random.choice(samples[xx])
                newgenes[xy].append(add)
                xy += 1
            except IndexError:
                continue

    print("第" + str(loop) + "世代")
    for x in newgenes:
        print(str(x))

    # ループに戻るためnewgenesをkidsに代入

    loop += 1
    kids = newgenes

 試したこと

・fatherのリスト置換に問題が起きていると考え、リストの一部を置換ではなくfather自体に
文字列や関係のないリストを代入してみたところ、motherは影響を受けませんでした。
つまりfatherの一部を置換しようと場合のみ、motherも変更されるバグが起きています。
この原因が不明です。

 補足情報(FW/ツールのバージョンなど)

PC: Mac
エディタ: Atom

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

同率一位になったときに、単為生殖してますね。

    max_score =  max(each_0_counts)
    premax_score = 0
    for x in each_0_counts:
        if x == max_score:
            continue
        elif x > premax_score:
            premax_score = x
        else:
            pass

    # 0の個数が全て同じ場合(同率1位)の処理

    if premax_score == 0:
        premax_score = max_score  # コメント追加:同じスコアになる
    else:
        pass

    # 1位、2位のリスト番号を取得し、fatherとmotherを決める

    father_index = each_0_counts.index(max_score)  # コメント追加:同じインデックスになる
    mother_index = each_0_counts.index(premax_score)    # コメント追加:同じインデックスになる

    father = kids[father_index]  # コメント追加:同じリストオブジェクトになる
    mother = kids[mother_index]  # コメント追加:同じリストオブジェクトになる

コード全体を理解していないのでこの対処で良いのかどうかはわかりませんが、空のスライス(同じ値の新しいリストオブジェクトを返す)を使って別オブジェクトにするのは易しいでしょう。

    father = kids[father_index] 
    mother = kids[mother_index][:] 

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/30 14:51

    解決しました。迅速な回答ありがとうございます。

    初学者なものでPythonにIDというものが存在することを知らなかったために行き詰まっていたようです。

    (メモ用:理解後に見つけた本件についての記事)
    https://qiita.com/utgwkk/items/5ad2527f19150ae33322

    ちなみに mother = 2位 と定義すると後半正しく成長しなかったので、最終的には1位だけを空のスライスでfather,motherに区分し、さらにfatherの一部に突然変異を行うことで95%の精度を達成できました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる