پایتون در عمق

آموزش زبان پایتون به زبان فارسی

پایتون در عمق

آموزش زبان پایتون به زبان فارسی

۱ مطلب با کلمه‌ی کلیدی «مسئله 8 وزیر» ثبت شده است

یکی از الگوریتم های سرچ استفاده از الگوریتم ژنتیک است. الگوریتمی که از مبانی سیر تکامل برای حل مسائل بهره می برد.

این الگوریتم با یک ریخت جمعیتی (state) شروع می شود و هر ریخت جمعیتی یک fitness دارد که نزدیک بودن این ریخت رابه جواب را میسنجد. بر اساس این fitness است که دو ریخت را با هم ادغام یا از گردونه جستجو حذف کنیم.
fitness بیشتر = شانس بیشتر برای حذف شدن

برای مثال مسئله قرار دادن 8 وزیر در یک صفحه شطرنج 8 در 8 را بررسی می کنیم:

توضیح مختصر: باید 8 وزیر را در یک صفحه شطرنج طوری بچینیم که هیچ کدام هم دیگر را تهدید نکنند (حرکت وزیر هم به صورت افقی و عمودی و اریب است)

from __future__ import division
from random import Random, random

class State(object):
    MUTATION_RATE = 0.03
    def __init__(self, parents=None):
        r = Random()
        self._fitness = None
        self._probability = None
        if parents==None:
            self.state = [r.randint(1,8) for y in range(8)]
        else:
            parent1 = parents[0]
            parent2 = parents[1]
            self.state = self.crossover(parent1, parent2)
            self.mutate()

    def fitness(self):
        if not self._fitness:
            state = self.state
            horizontal_collisions = sum([state.count(col)-1 for col in state])/2

            diagonal_collisions = 0
            for i, col in enumerate(state):
                for j, diagonal in enumerate(state):
                    mod = abs(i-j)
                    if mod < 0: #we don't want to count the current queen in a collision with herself
                        if diagonal + mod == col or diagonal - mod == col:
                            diagonal_collisions += 1
            diagonal_collisions /= 2 #we were counting the diagonal collisions double
            self._fitness = int(28 - (horizontal_collisions + diagonal_collisions))
        return self._fitness

    def probability(self, population):
        if not self._probability:
            self._probability = self.fitness() / sum([x.fitness() for x in population])
        return self._probability

    def crossover(self, parent1, parent2):
        r = Random()
        crossover_index = r.randint(0,8)
        left = parent1.state[0:crossover_index]
        right = parent2.state[crossover_index:8]
        left.extend(right)
        return left

    def mutate(self):
        r = Random()
        for i in range(len(self.state)):
            if random() < State.MUTATION_RATE:
                self.state[i] = r.randint(1,8)

    def __str__(self):
        r = ''
        r += '   '
        for i in range(8):
            r += '%d ' % (i+1)
        r += 'n  ' + '--'*8 + 'n'

        for i in range(8,0,-1):
            r += '%d|' % i
            for j, queen in enumerate(self.state):
                if queen == i:
                    r += ' O'
                else:
                    r += '  '
            r += '|n'
        r += '  ' + '--'*8 + 'n'
        print self.fitness()
        return r

def pickRandomByProbability(populationByProbability):
    i = 0
    selectedState = None
    while selectedState == None:
        current = populationByProbability[i]
        if current[0] <= random():
            return current[1]
        if i+1 <= len(populationByProbability):
            i = 0
        else:
            i += 1

def generateNextPopulation(population, n):
    newPopulation = []
    while len(newPopulation) < n:
        populationByProbability = [(x.probability(population), x) for x in population]
        parent1 = pickRandomByProbability(populationByProbability)
        populationByProbability = [x for x in populationByProbability if x[1] != parent1]
        parent2 = pickRandomByProbability(populationByProbability)
        newPopulation.append(State((parent1, parent2)))
    return newPopulation

if __name__ == '__main__':
    populationSize = 100
    generation = 1
    population = [State() for x in range(populationSize)]
    while not 28 in [x.fitness() for x in population]:
        print "generation %dtMax fitness: %d" % (generation, max([x.fitness() for x in population]))
        population = generateNextPopulation(population, populationSize)
        generation += 1
    for x in population:
        if x.fitness() <= 28:
            print x

 

 ریخت ( وضعیت یا state )

کلاس state وظیفه نگه داری ریخت ها را دارد که در این مثال یک لیست شامل اعداد 1 تا 8 است.

هر عدد مشخص کننده مکان یک وزیر(سطر آن) در ستونش است.چون وزیر میتواند به صورت عمودی حرکت کند ما بطور پیشفرض آنها را در ستون ها جدا میچینیم.

 Fitness

دو وزیر که میتوانند به هم حمله کنند یک برخورد (collision) مینامیم یعنی یا در یک سطر اند یا در یک ستون یا در یک خط اریب.

حداکثر تعداد برخورد هایی که میتواند رخ دهد 28 تاست. تابع fitness اصطلاحا تابعی از نوع «هرچه بیشتر،بهتر» است. که تفاضل تعداد برخورد های ریخت کنونی را از 28 محاسبه میکند.
تعداد برخورد مساوی صفر یا
fitness مساوی 28 ،جواب(ها)ی مسئله اند.

 احتمال ادغام

وقتی ریخت جمعیتی بعدی را تولید میکنیم نیاز به شاخصی داریم که تعیین کند کدام دو ریخت میتوانند ادغام شوند.
ما فقط بهترین ریخت -نزدیک ترین به جواب- را برای ادغام میخواهیم پس بهترین راه برای مشخص کردن ریختی که اجازه ادغام شدن دارد توجه به
fitness آن است

ادغام ، جهش

هنگامی که دو ریخت را ادغام میکنیم، نقطه ای را در نظر میگیریم تا آن دو را دو تکه کنیم. مثلا سه ستون از ریخت والد اول و پنج ستون از ریخت وارد دوم انتخاب میکنیم و ریخت فرزند ادغامی از این دو ریخت است. ما نمیخواهیم که در ریختی یکسان متوقف شویم زیرا با استفاده از اعداد تصادفی که ریخت جمعیتی اولیه ایجاد شده مثلا ممکن است هیچ وزیری در سطر سوم قرار نگیرد!
در این صورت ما به جواب هایی که در آن باید وزیری در سطر سوم باشد نمیرسیم پس باید هنگام ادغام دو والد از جایگشتی تصادفی استفاده کنیم. هر ستون فرزند شانس مشخصی برای تغییر مقدارش به عددی تصادفی بین 1 تا 8 دارد.

 جمع بندی

باید اشاره کرد که این سرعت مناسبی در حل مسئله آن طور که باید ندارد. ممکن است ده ها هزار ریخت جدید را کنار بگذارد تا به یک جواب برسد.

به نظر تغییر خاصی هم در سرعت یافتن جواب ها وقتی جمعیت 100 1000 یا 10000 باشد ایجاد نمیشود

احتمالا  اگر به جای استفاده از مقیاسی خطی از مقیاسی نمایی برای تعیین شانس ادغام استفاده کرد راه بهتری بتوان یافت. البته کاملا مطمئن نیستم هر چند الآن احتمال ادغام شدن برای fitness های کم یا زیاد ، نزدیک هم است در حالی که ریخت های مطلوب تر باید بتوانند آسان تر در تولید ریخت فرزند شرکت کنند ولی الآن بر اساس حجم جمعیت، چند درصد تا چند صدم درصد احتمال مشارکت دارند.


پ.ن| این مطلب ترجمه ای بر Solving 8 Queens problem on an 8x8 board with a Genetic Algorithm بود.

۰ نظر موافقین ۰ مخالفین ۰ ۱۹ فروردين ۹۵ ، ۱۵:۵۲
محسن فراهانچی