AI-For-Beginners/translations/fa/lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb

709 lines
35 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# الگوریتم‌های ژنتیک\n",
"\n",
"این دفترچه بخشی از [برنامه آموزشی هوش مصنوعی برای مبتدیان](http://github.com/microsoft/ai-for-beginners) است.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"import random\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import math\n",
"import time"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## مقداری نظریه\n",
"\n",
"**الگوریتم‌های ژنتیکی** (GA) بر اساس **رویکرد تکاملی** در هوش مصنوعی هستند، که در آن از روش‌های تکامل جمعیت برای یافتن یک راه‌حل بهینه برای یک مسئله مشخص استفاده می‌شود. این الگوریتم‌ها در سال ۱۹۷۵ توسط [جان هنری هلند](https://en.wikipedia.org/wiki/John_Henry_Holland) پیشنهاد شدند.\n",
"\n",
"الگوریتم‌های ژنتیکی بر اساس ایده‌های زیر بنا شده‌اند:\n",
"* راه‌حل‌های معتبر برای مسئله می‌توانند به صورت **ژن‌ها** نمایش داده شوند.\n",
"* **ترکیب** به ما اجازه می‌دهد دو راه‌حل را با هم ترکیب کنیم تا یک راه‌حل جدید و معتبر به دست آوریم.\n",
"* **انتخاب** برای گزینش راه‌حل‌های بهینه‌تر با استفاده از یک **تابع تناسب** به کار می‌رود.\n",
"* **جهش‌ها** برای بی‌ثبات کردن بهینه‌سازی و خروج از حداقل محلی معرفی می‌شوند.\n",
"\n",
"اگر بخواهید یک الگوریتم ژنتیکی را پیاده‌سازی کنید، به موارد زیر نیاز دارید:\n",
"\n",
"* یافتن روشی برای کدگذاری راه‌حل‌های مسئله با استفاده از **ژن‌ها** $g\\in\\Gamma$\n",
"* تعریف **تابع تناسب** $\\mathrm{fit}: \\Gamma\\to\\mathbb{R}$ روی مجموعه ژن‌ها $\\Gamma$. مقادیر کوچکتر تابع نشان‌دهنده راه‌حل‌های بهتر هستند.\n",
"* تعریف مکانیزم **ترکیب** برای ترکیب دو ژن و به دست آوردن یک راه‌حل معتبر جدید $\\mathrm{crossover}: \\Gamma^2\\to\\Gamma$.\n",
"* تعریف مکانیزم **جهش** $\\mathrm{mutate}: \\Gamma\\to\\Gamma$.\n",
"در بسیاری از موارد، ترکیب و جهش الگوریتم‌های ساده‌ای هستند که ژن‌ها را به عنوان دنباله‌های عددی یا بردارهای بیتی دستکاری می‌کنند.\n",
"\n",
"پیاده‌سازی خاص یک الگوریتم ژنتیکی ممکن است از موردی به مورد دیگر متفاوت باشد، اما ساختار کلی به صورت زیر است:\n",
"\n",
"1. انتخاب جمعیت اولیه $G\\subset\\Gamma$\n",
"2. به صورت تصادفی یکی از عملیات‌هایی که در این مرحله انجام خواهد شد را انتخاب کنید: ترکیب یا جهش\n",
"3. **ترکیب**:\n",
" * به صورت تصادفی دو ژن $g_1, g_2 \\in G$ را انتخاب کنید.\n",
" * ترکیب را محاسبه کنید $g=\\mathrm{crossover}(g_1,g_2)$\n",
" * اگر $\\mathrm{fit}(g)<\\mathrm{fit}(g_1)$ یا $\\mathrm{fit}(g)<\\mathrm{fit}(g_2)$ - ژن مربوطه در جمعیت را با $g$ جایگزین کنید.\n",
"4. **جهش** - یک ژن تصادفی $g\\in G$ را انتخاب کنید و آن را با $\\mathrm{mutate}(g)$ جایگزین کنید.\n",
"5. از مرحله ۲ تکرار کنید، تا زمانی که مقدار $\\mathrm{fit}$ به اندازه کافی کوچک شود یا محدودیت تعداد مراحل به پایان برسد.\n",
"\n",
"وظایفی که معمولاً توسط الگوریتم‌های ژنتیکی حل می‌شوند:\n",
"1. بهینه‌سازی زمان‌بندی\n",
"1. بسته‌بندی بهینه\n",
"1. برش بهینه\n",
"1. تسریع جستجوی جامع\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## مشکل ۱: تقسیم عادلانه گنج\n",
"\n",
"**وظیفه**: \n",
"دو نفر گنجی پیدا کرده‌اند که شامل الماس‌هایی با اندازه‌های مختلف (و به تبع آن، قیمت‌های مختلف) است. آن‌ها باید گنج را به دو بخش تقسیم کنند به‌گونه‌ای که تفاوت قیمت بین دو بخش برابر با صفر (یا حداقل ممکن) باشد.\n",
"\n",
"**تعریف رسمی**: \n",
"ما مجموعه‌ای از اعداد $S$ داریم. باید آن را به دو زیرمجموعه $S_1$ و $S_2$ تقسیم کنیم، به‌طوری که: \n",
"$$\\left|\\sum_{i\\in S_1}i - \\sum_{j\\in S_2}j\\right|\\to\\min$$ \n",
"و $S_1\\cup S_2=S$، $S_1\\cap S_2=\\emptyset$.\n",
"\n",
"ابتدا، مجموعه $S$ را تعریف کنیم:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[8344 2197 9335 3131 5863 9429 3818 9791 15 5455 1396 9538 4872 6549\n",
" 8587 5986 6021 9764 8102 5083 5739 7684 8498 3007 6599 820 7490 2372\n",
" 9370 5235 3525 3154 859 1906 8159 3950 2173 2988 2050 349 8713 2284\n",
" 4177 6033 1651 9176 5049 8201 171 5081 1216 3756 4711 2757 7738 1272\n",
" 5650 6584 5395 9004 7797 969 8104 1283 1392 4001 5768 445 274 256\n",
" 8239 8015 4381 9021 1189 8879 1411 3539 6526 8011 136 7230 2332 451\n",
" 5702 2989 4320 2446 9578 8486 4027 2410 9588 8981 2177 1493 3232 9151\n",
" 4835 5594 6859 8394 369 3200 126 4259 2283 7755 2014 2458 8327 8082\n",
" 7413 7622 1206 5533 8751 3495 5868 8472 6850 3958 3149 4672 4810 6274\n",
" 4700 6134 4627 4616 6656 9949 884 2256 7419 1926 7973 5319 5967 9158\n",
" 3823 7697 9466 5675 5412 9784 5426 8209 3421 1136 6047 4429 8001 4417\n",
" 1381 722 7350 6018 6235 7860 5853 7660 5937 6242 1 9552 3971 8302\n",
" 2633 9227 7283 154 8599 4269 9392 8539 1630 368 2409 9351 3838 9814\n",
" 6186 5743 5083 1325 1610 779 3643 3262 5768 8725 961 4611 6310 4788\n",
" 1648 5951 8118 7779]\n"
]
}
],
"source": [
"N = 200\n",
"S = np.array([random.randint(1,10000) for _ in range(N)])\n",
"print(S)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"بیایید هر راه‌حل ممکن مسئله را با یک بردار باینری $B\\in\\{0,1\\}^N$ کدگذاری کنیم، که عدد در موقعیت $i$-ام نشان می‌دهد که عدد $i$-ام در مجموعه اصلی $S$ به کدام یک از مجموعه‌ها ($S_1$ یا $S_2$) تعلق دارد. تابع `generate` این بردارهای باینری تصادفی را تولید خواهد کرد.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 0 1 0 1 1 1\n",
" 0 1 1 1 0 1 0 0 1 0 0 1 1 0 1 0 1 1 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 1 0 1 0\n",
" 1 0 0 0 0 0 1 1 0 1 0 0 1 0 1 0 0 1 1 0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 0 1 1\n",
" 1 0 1 0 0 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 0 1 0 1 0 1 0 0 1 1 1 0 0 1 1\n",
" 0 1 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 1 1 0 1 1 0 0 1 1 0\n",
" 0 0 0 1 0 1 1 0 1 1 0 1 0 0 0]\n"
]
}
],
"source": [
"def generate(S):\n",
" return np.array([random.randint(0,1) for _ in S])\n",
"\n",
"b = generate(S)\n",
"print(b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"بیایید اکنون تابع `fit` را تعریف کنیم که \"هزینه\" راه‌حل را محاسبه می‌کند. این هزینه تفاوت بین مجموع دو مجموعه، $S_1$ و $S_2$ خواهد بود:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"133784"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def fit(B,S=S):\n",
" c1 = (B*S).sum()\n",
" c2 = ((1-B)*S).sum()\n",
" return abs(c1-c2)\n",
"\n",
"fit(b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"اکنون باید توابعی برای جهش و ترکیب تعریف کنیم:\n",
"* برای جهش، یک بیت تصادفی انتخاب می‌کنیم و آن را معکوس می‌کنیم (تغییر از ۰ به ۱ و بالعکس)\n",
"* برای ترکیب، تعدادی بیت از یک بردار و تعدادی بیت از بردار دیگر می‌گیریم. از همان تابع `generate` استفاده می‌کنیم تا به صورت تصادفی انتخاب کنیم که کدام بیت‌ها را از کدام یک از ماسک‌های ورودی بگیریم.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"def mutate(b):\n",
" x = b.copy()\n",
" i = random.randint(0,len(b)-1)\n",
" x[i] = 1-x[i]\n",
" return x\n",
"\n",
"def xover(b1,b2):\n",
" x = generate(b1)\n",
" return b1*x+b2*(1-x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"بیایید جمعیت اولیه‌ای از راه‌حل‌ها $P$ با اندازه `pop_size` ایجاد کنیم:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"pop_size = 30\n",
"P = [generate(S) for _ in range(pop_size)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"اکنون، تابع اصلی برای انجام تکامل. `n` تعداد مراحل تکامل است که باید انجام شود. در هر مرحله:\n",
"* با احتمال ۳۰٪ یک جهش انجام می‌دهیم و عنصر با بدترین تابع `fit` را با عنصر جهش‌یافته جایگزین می‌کنیم\n",
"* با احتمال ۷۰٪ یک ترکیب انجام می‌دهیم\n",
"\n",
"این تابع بهترین راه‌حل (ژن مربوط به بهترین راه‌حل) و تاریخچه کمترین مقدار تابع `fit` در جمعیت در هر تکرار را بازمی‌گرداند.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0 0 0 1 1 0 0 0 0 1 1 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 0\n",
" 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 0 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0\n",
" 0 1 1 1 0 1 0 1 1 1 1 1 0 0 0 1 1 0 1 0 0 1 0 0 1 1 1 1 1 1 1 1 0 1 0 1 1\n",
" 0 1 1 0 0 0 0 1 1 1 1 0 1 0 0 1 0 1 1 1 0 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0\n",
" 1 0 1 1 1 1 1 0 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 1 1 0 1 0 0 0 1 0 1\n",
" 0 1 0 1 0 0 1 1 1 0 1 1 0 0 1] 4\n"
]
}
],
"source": [
"def evolve(P,S=S,n=2000):\n",
" res = []\n",
" for _ in range(n):\n",
" f = min([fit(b) for b in P])\n",
" res.append(f)\n",
" if f==0:\n",
" break\n",
" if random.randint(1,10)<3:\n",
" i = random.randint(0,len(P)-1)\n",
" b = mutate(P[i])\n",
" i = np.argmax([fit(z) for z in P])\n",
" P[i] = b\n",
" else:\n",
" i = random.randint(0,len(P)-1)\n",
" j = random.randint(0,len(P)-1)\n",
" b = xover(P[i],P[j])\n",
" if fit(b)<fit(P[i]):\n",
" P[i]=b\n",
" elif fit(b)<fit(P[j]):\n",
" P[j]=b\n",
" else:\n",
" pass\n",
" i = np.argmin([fit(b) for b in P])\n",
" return (P[i],res)\n",
"\n",
"(s,hist) = evolve(P)\n",
"print(s,fit(s))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"شما می‌توانید ببینید که ما توانسته‌ایم تابع `fit` را تا حد زیادی کاهش دهیم! در اینجا نموداری است که نشان می‌دهد چگونه تابع `fit` برای کل جمعیت در طول فرآیند رفتار می‌کند.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.plot(hist)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## مسئله ۲: مسئله وزیرهای N\n",
"\n",
"**وظیفه**: \n",
"شما باید $N$ وزیر را روی یک صفحه شطرنج به ابعاد $N\\times N$ قرار دهید به‌گونه‌ای که هیچ‌کدام از آن‌ها به یکدیگر حمله نکنند.\n",
"\n",
"ابتدا، بیایید این مسئله را بدون استفاده از الگوریتم‌های ژنتیک و با استفاده از جستجوی کامل حل کنیم. می‌توانیم وضعیت صفحه شطرنج را با لیست $L$ نمایش دهیم، به‌طوری‌که عدد $i$-ام در لیست نشان‌دهنده موقعیت افقی وزیر در سطر $i$-ام است. کاملاً واضح است که هر راه‌حل فقط یک وزیر در هر سطر خواهد داشت و هر سطر یک وزیر خواهد داشت.\n",
"\n",
"هدف ما این است که اولین راه‌حل مسئله را پیدا کنیم و سپس جستجو را متوقف کنیم. شما می‌توانید به‌راحتی این تابع را گسترش دهید تا تمام موقعیت‌های ممکن برای وزیرها را تولید کند.\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 5, 8, 6, 3, 7, 2, 4]\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"N = 8\n",
"\n",
"def checkbeats(i_new,j_new,l):\n",
" for i,j in enumerate(l,start=1):\n",
" if j==j_new:\n",
" return False\n",
" else:\n",
" if abs(j-j_new) == i_new-i:\n",
" return False\n",
" return True\n",
"\n",
"def nqueens(l,N=8,disp=True):\n",
" if len(l)==N:\n",
" if disp: print(l)\n",
" return True\n",
" else:\n",
" for j in range(1,N+1):\n",
" if checkbeats(len(l)+1,j,l):\n",
" l.append(j)\n",
" if nqueens(l,N,disp): return True\n",
" else: l.pop()\n",
" return False\n",
" \n",
"nqueens([],8)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"حالا بیایید اندازه بگیریم که حل مسئله ۲۰ وزیر چقدر زمان می‌برد:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10.6 s ± 2.17 s per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"%timeit nqueens([],20,False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"حالا بیایید همین مسئله را با استفاده از الگوریتم ژنتیک حل کنیم. این راه‌حل از [این پست وبلاگ](https://kushalvyas.github.io/gen_8Q.html) الهام گرفته شده است.\n",
"\n",
"ما هر راه‌حل را با همان لیست به طول $N$ نمایش می‌دهیم، و به عنوان تابع `fit` تعداد وزیرهایی را که به یکدیگر حمله می‌کنند در نظر می‌گیریم:\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"def fit(L):\n",
" x=0\n",
" for i1,j1 in enumerate(L,1):\n",
" for i2,j2 in enumerate(L,1):\n",
" if i2>i1:\n",
" if j2==j1 or (abs(j2-j1)==i2-i1): x+=1\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"از آنجا که محاسبه تابع برازش زمان‌بر است، بیایید هر راه‌حل را در جمعیت همراه با مقدار تابع برازش ذخیره کنیم. بیایید جمعیت اولیه را تولید کنیم:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"[(array([2, 3, 8, 7, 5, 4, 1, 6]), 4),\n",
" (array([3, 4, 5, 1, 2, 8, 6, 7]), 8),\n",
" (array([1, 3, 7, 4, 5, 8, 6, 2]), 6),\n",
" (array([1, 5, 4, 6, 8, 3, 7, 2]), 4),\n",
" (array([3, 5, 7, 1, 8, 6, 4, 2]), 3)]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def generate_one(N):\n",
" x = np.arange(1,N+1)\n",
" np.random.shuffle(x)\n",
" return (x,fit(x))\n",
"\n",
"def generate(N,NP):\n",
" return [generate_one(N) for _ in range(NP)]\n",
"\n",
"generate(8,5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"اکنون باید توابع جهش و ترکیب را تعریف کنیم. ترکیب، دو ژن را با شکستن آن‌ها در یک نقطه تصادفی و اتصال دو بخش از ژن‌های مختلف به یکدیگر ترکیب می‌کند.\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2, 7, 8])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def mutate(G):\n",
" x=random.randint(0,len(G)-1)\n",
" G[x]=random.randint(1,len(G))\n",
" return G\n",
" \n",
"def xover(G1,G2):\n",
" x=random.randint(0,len(G1))\n",
" return np.concatenate((G1[:x],G2[x:]))\n",
"\n",
"xover([1,2,3,4],[5,6,7,8])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"def choose_rand(P):\n",
" N=len(P[0][0])\n",
" mf = N*(N-1)//2 # max fitness fn\n",
" z = [mf-x[1] for x in P]\n",
" tf = sum(z) # total fitness\n",
" w = [x/tf for x in z]\n",
" p = np.random.choice(len(P),2,False,p=w)\n",
" return p[0],p[1]\n",
"\n",
"def choose(P):\n",
" def ch(w):\n",
" p=[]\n",
" while p==[]:\n",
" r = random.random()\n",
" p = [i for i,x in enumerate(P) if x[1]>=r]\n",
" return random.choice(p)\n",
" N=len(P[0][0])\n",
" mf = N*(N-1)//2 # max fitness fn\n",
" z = [mf-x[1] for x in P]\n",
" tf = sum(z) # total fitness\n",
" w = [x/tf for x in z]\n",
" p1=p2=0\n",
" while p1==p2:\n",
" p1 = ch(w)\n",
" p2 = ch(w)\n",
" return p1,p2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"حالا بیایید حلقه اصلی تکاملی را تعریف کنیم. منطق را کمی متفاوت از مثال قبلی طراحی می‌کنیم تا نشان دهیم که می‌توان خلاقیت به خرج داد. ما تا زمانی که به راه‌حل کامل (تابع تناسب=۰) برسیم، حلقه را ادامه می‌دهیم و در هر مرحله نسل فعلی را می‌گیریم و نسل جدیدی با همان اندازه تولید می‌کنیم. این کار با استفاده از تابع `nxgeneration` و مراحل زیر انجام می‌شود:\n",
"\n",
"1. حذف راه‌حل‌هایی که کمترین تناسب را دارند - تابعی به نام `discard_unfit` این کار را انجام می‌دهد.\n",
"1. اضافه کردن چند راه‌حل تصادفی دیگر به نسل.\n",
"1. تولید نسل جدید با اندازه `gen_size` با استفاده از مراحل زیر برای هر ژن جدید:\n",
" - انتخاب دو ژن تصادفی، با احتمالی متناسب با تابع تناسب.\n",
" - محاسبه یک ترکیب (crossover).\n",
" - اعمال یک جهش با احتمال `mutation_prob`.\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"(array([4, 7, 5, 3, 1, 6, 8, 2]), 0)"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mutation_prob = 0.1\n",
"\n",
"def discard_unfit(P):\n",
" P.sort(key=lambda x:x[1])\n",
" return P[:len(P)//3]\n",
"\n",
"def nxgeneration(P):\n",
" gen_size=len(P)\n",
" P = discard_unfit(P)\n",
" P.extend(generate(len(P[0][0]),3))\n",
" new_gen = []\n",
" for _ in range(gen_size):\n",
" p1,p2 = choose_rand(P)\n",
" n = xover(P[p1][0],P[p2][0])\n",
" if random.random()<mutation_prob:\n",
" n=mutate(n)\n",
" nf = fit(n)\n",
" new_gen.append((n,nf))\n",
" '''\n",
" if (nf<=P[p1][1]) or (nf<=P[p2][1]):\n",
" new_gen.append((n,nf))\n",
" elif (P[p1][1]<P[p2][1]):\n",
" new_gen.append(P[p1])\n",
" else:\n",
" new_gen.append(P[p2])\n",
" '''\n",
" return new_gen\n",
" \n",
"def genetic(N,pop_size=100):\n",
" P = generate(N,pop_size)\n",
" mf = min([x[1] for x in P])\n",
" n=0\n",
" while mf>0:\n",
" #print(\"Generation {0}, fit={1}\".format(n,mf))\n",
" n+=1\n",
" mf = min([x[1] for x in P])\n",
" P = nxgeneration(P)\n",
" mi = np.argmin([x[1] for x in P])\n",
" return P[mi]\n",
"\n",
"genetic(8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"جالب است که در بیشتر مواقع ما می‌توانیم راه‌حلی را به سرعت پیدا کنیم، اما در برخی موارد نادر بهینه‌سازی به حداقل محلی می‌رسد و فرآیند برای مدت طولانی متوقف می‌شود. مهم است که این موضوع را هنگام اندازه‌گیری زمان متوسط در نظر بگیرید: در حالی که در بیشتر موارد الگوریتم ژنتیک سریع‌تر از جستجوی کامل خواهد بود، در برخی موارد ممکن است زمان بیشتری ببرد. برای غلبه بر این مشکل، اغلب منطقی است که تعداد نسل‌ها را محدود کنیم و اگر نتوانیم راه‌حل را پیدا کنیم - می‌توانیم از ابتدا شروع کنیم.\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The slowest run took 18.71 times longer than the fastest. This could mean that an intermediate result is being cached.\n",
"26.4 s ± 28.7 s per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"%timeit genetic(10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**سلب مسئولیت**: \nاین سند با استفاده از سرویس ترجمه هوش مصنوعی [Co-op Translator](https://github.com/Azure/co-op-translator) ترجمه شده است. در حالی که ما تلاش می‌کنیم دقت را حفظ کنیم، لطفاً توجه داشته باشید که ترجمه‌های خودکار ممکن است شامل خطاها یا نادرستی‌ها باشند. سند اصلی به زبان اصلی آن باید به عنوان منبع معتبر در نظر گرفته شود. برای اطلاعات حساس، توصیه می‌شود از ترجمه حرفه‌ای انسانی استفاده کنید. ما مسئولیتی در قبال سوء تفاهم‌ها یا تفسیرهای نادرست ناشی از استفاده از این ترجمه نداریم.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
},
"kernelspec": {
"display_name": "Python 3.6",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
},
"coopTranslator": {
"original_hash": "1012c117808753433f6e2696e775ff4b",
"translation_date": "2025-08-31T15:42:46+00:00",
"source_file": "lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb",
"language_code": "fa"
}
},
"nbformat": 4,
"nbformat_minor": 2
}