From 3691f9ca3706dd8cd23f53686257ec7b84ebaa64 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 May 2025 15:06:24 +0400 Subject: [PATCH] ready --- .gitignore | 2 + lab_10/lab10.ipynb | 579 ++++++++++++--------------------------------- 2 files changed, 150 insertions(+), 431 deletions(-) diff --git a/.gitignore b/.gitignore index 207d123..79746f5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ ipython_config.py # Remove previous ipynb_checkpoints # git rm -r .ipynb_checkpoints/ +aimvenv +Static \ No newline at end of file diff --git a/lab_10/lab10.ipynb b/lab_10/lab10.ipynb index 2fc93ec..0c4dc5a 100644 --- a/lab_10/lab10.ipynb +++ b/lab_10/lab10.ipynb @@ -13,53 +13,51 @@ "id": "fbcde77d", "metadata": {}, "source": [ - "### 1. Определение задачи оптимизации\n", + "## 1. Определение задачи оптимизации\n", "\n", - "**Цель оптимизации** — найти такую цену товара, при которой общая выручка от продаж будет наибольшей.\n", - "Для этого мы используем исторические данные, на их основе строим зависимость между ценой и объёмом продаж, а затем с помощью алгоритма определяем оптимальное значение.\n", + "Цель — определить оптимальную розничную цену на товар, при которой прибыль будет максимальной, учитывая переменные из датасета:\n", + "- закупочную стоимость (`unit_price`),\n", + "- стоимость логистики (`freight_price`),\n", + "- объём продаж (`qty`).\n", "\n", - "Целевая функция: **revenue(x) = x ⋅ D(x)**\n", - "где:\n", - "- **x** — цена товара (оптимизируемый параметр),\n", - "- **D(x)** — функция спроса, отражающая прогнозируемый объём продаж при данной цене.\n", + "Тип задачи — **максимизация** функции прибыли:\n" + ] + }, + { + "cell_type": "markdown", + "id": "92e9e640", + "metadata": {}, + "source": [ + "## 2. Набор данных\n", "\n", - "**Характеристики задачи:**\n", - "- Тип: одномерная непрерывная оптимизация\n", - "- Цель: максимизация функции\n", + "Датасет: `retail_price.csv` \n", + "Отфильтрованы значения по конкретному товару — `bed1`.\n", "\n", - "Оптимизация — это раздел математики, направленный на поиск экстремумов функции (максимума или минимума).\n", - "В данной задаче оптимизация выполняется по одному параметру — цене.\n", - "Так как у нас отсутствует точная аналитическая форма зависимости выручки от цены, мы применяем линейную регрессию для приближённого моделирования этой связи.\n", - "Поскольку функция может быть сложной и включать локальные максимумы и минимумы, используется генетический алгоритм — метод эволюционной оптимизации, эффективный в задачах с труднопредсказуемым ландшафтом.\n" + "Выделены поля:\n", + "- `unit_price` — закупочная цена,\n", + "- `freight_price` — доставка,\n", + "- `qty` — проданное количество,\n", + "- `total_price` — общая выручка,\n", + "- `lag_price` — цена продажи в предыдущем месяце (ориентир).\n" ] }, { "cell_type": "code", - "execution_count": 14, - "id": "80dcedd6", + "execution_count": 3, + "id": "6c041d79", "metadata": {}, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "C:\\Users\\Natalia\\AppData\\Local\\Temp\\ipykernel_936\\24853934.py:11: SettingWithCopyWarning: \n", - "A value is trying to be set on a copy of a slice from a DataFrame.\n", - "Try using .loc[row_indexer,col_indexer] = value instead\n", - "\n", - "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", - " product_data['revenue'] = product_data['unit_price'] * product_data['qty']\n" + " lag_price cost qty profit\n", + "0 45.90 61.050000 1 -15.10\n", + "1 45.95 58.883333 3 -38.80\n", + "2 45.95 60.790000 6 -89.04\n", + "3 45.95 60.237500 4 -57.15\n", + "4 45.95 61.050000 2 -30.20\n" ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAawVJREFUeJzt3QmcjXX///HP7ItZzMhYMoZQCJEW1J2yJq1Ud4uo3C1CoVSihUK5u9OtH3W3SYuUSncl2dpDhErIUu5RtlFmMWYxy/k/Pt9c539mnBnnMJczZ67X8/E4j+Nc13XO+Z7zPeeY9/XdQlwul0sAAAAAAECVC636hwQAAAAAAIrQDQAAAACATQjdAAAAAADYhNANAAAAAIBNCN0AAAAAANiE0A0AAAAAgE0I3QAAAAAA2ITQDQAAAACATQjdAAAAAADYhNANAACAoHXjjTdKXFzccXmuJk2amOcDAH8QugE4wnPPPSe9e/eWevXqSUREhNSvX1+6du0qr776qpSWlga6eACAIDVx4kS59NJLzf8vISEh8sgjjwS6SACqmfBAFwAAjodZs2ZJgwYN5MEHH5SEhATJysqSFStWmBaLBQsWyJtvvhnoIgIAgtC4cePMidwOHTrIwoULA10cANUQoRuAI3z55ZemhdvTnXfeKXXq1JH/+7//k8mTJ5tugwAA+GPbtm3m/48//vhD6tatG+jiAKiG6F4OwBHKB26LFbRDQ///z+F///tf6du3rzRs2FCioqKkWbNm8uijj0pJSUmZ+55//vmmK6F1OeGEE8z9fvrppzLHeetu+M9//tNs18fwVFBQYI49+eSTJTo62rTO9+vXT3755Rez/3//+5+53yuvvFLmfkOHDjXbPcca6jG6LTIyUvbu3Vvm+OXLl7vL/d1335XZN3fuXOnYsaPExMSY1zRgwADZsWPHYe/dzz//LFdffbX5I1OPPeWUU2Ts2LFmn74Gz/fG2+Xzzz93v49t2rSRo+FPHQwbNqzCx7HeK31/PT8bF198sSxatEjat29v6qN169by3nvvuY/59ddfzf2mTp162GMuW7bM7LN6UWhZy9e3t/rUOix/Auj11183n9HHH3+80uN+++03UxflX0tFPv30U/nb3/4mtWrVktq1a8tll10mGzdudO/3px69se6vYcSTfua8fY51qMfTTz8tp556qnm/tbvu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991nWtEjI5ltGgAAAADg4NCtY8MbNmwo0dHR0rlzZ5k8ebI0btzYzIheVFRUZkZ07Xqu+5YvX25Ct163bdvWBG5L7969zUzq69evlw4dOnh9zsLCQnOx5OTkmGt9Pr0ciXWML8ciuFHXzkJ9Owv17SzUt7NQ385CfTtLUTWqb1/LENDQffbZZ8srr7wip5xyiukaPn78ePnb3/4mP/30k+zevdu0VNeuXbvMfTRg6z6l156B29pv7auIBnt9rvK05VzHhvtq8eLFPh+L4EZdOwv17SzUt7NQ385CfTsL9e0si6tBfefl5VX/0N2nTx/3v9u1a2dCeFpamrz99tsSExNj2/OOGTNGRo0aVaalOzU1VXr16iUJCQk+ndHQSu7Zs6cZP46ai7p2FurbWahvZ6G+nYX6dhbq21mKqlF9Wz2mqzR0a5LX5cKuvPLKw/a9//770r17d4mPj5ejpa3aOhP61q1bzZt48OBBycrKKtParbOXW2PA9XrlypVlHsOa3dzbOHGLtb54eVpp/lScv8cjeFHXzkJ9Owv17SzUt7NQ385CfTtLRDWob1+f368lw3Sc9E033SRTpkwps3369OkyYMAA+f777+VY5Obmyi+//CINGjSQjh07mhexdOlS9/5NmzaZJcJ07LfS63Xr1klGRob7GD3roa3VrVu3PqayAAAAAABwrPxq6T7zzDNlwYIFZpmu/Px8efjhh+Wpp54y19rSreOx/XHPPffIJZdcYrqU79y50zxOWFiYXHvttWaJsMGDB5tu4MnJySZIDx8+3ARtnURNaXdwDdc33HCDORGg47jHjRtn1vb21pINAAAAAMDx5PeY7nPPPddMOKbjsT/77DNZu3atWUv7vPPO8/vJf//9dxOw//zzT6lbt655bF0OTP+tpk6dKqGhodK/f38z27jOTD5jxgz3/TWgf/TRR2a2cg3jtWrVkkGDBsmECRP8LgsAAAAAAFXtqCZS0wnPdFz3iy++KNddd91RBW41Z86cSvfrMmLadV0vFdFWch1nDgAAAABAdePXmG7P2b/fe+89mTVrlhlzPWLEiKovGQAAAAAATmvp1jHWr7/+ugnbp512mpx11llm1nLt/v3ss8/aU0oAAAAAAGp6S/cXX3whb731lrnWwK1OOeUUc1snWNMLAAAAAAA4ipburl27yubNm82EZZ6aNWtmlhMr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2XB86lu/y3p58skn5aeffpJXXnlFPvnkExk8eHBAywr7vt8dO3aUmTNnysaNG2XhwoUmjOn/6SUlJQErK+yrb8vTTz9t/m5Dza/vW265RXbt2uW+TJkyJSBlhP31vXz5cvP3mv6Gr1y50hw3bNgwCQ2thhFXZy+Hb5KSklwvvviia+HCha7Q0FBXdna2e19WVpYrJCTEtXjx4oCWEUdv//79rhYtWpg67Nq1q+uuu+5y121ERIRr7ty57mM3btyos/67li9fHsASw4769vTZZ5+Zes7MzAxIGXF869vy9ttvuyIjI11FRUXHtYwITH3/8MMP5nu+devW41pGHL/6Xrt2revEE0907dq1y9T1vHnzAlZW2FvfR/q+o2bV99lnn+0aN26cKxhUw9MA1Y+e/Z4zZ45p4dZu5oWFheZsqefacNHR0easytdffx3QsuLoaffxvn37So8ePcps194ORUVFZba3bNnSdEPVM2yoWfWNmsmf+tau5dpFLTw8/LiUDYGrb/1/XVu9mzZtKqmpqcetfDh+9a09l6677jqZPn261K9fPyDlw/H9fr/xxhtywgknSJs2bWTMmDHmM4CaV98ZGRny7bffSkpKinTp0kXq1asnXbt2rbZZjL8oKrFu3ToTsnU8r47jnTdvnrRu3dqMAdNxA/fdd59MmjTJdE27//77TTjXbiwIPnpSZc2aNaZbSnm7d++WyMhIqV27dpnt+uXWfahZ9Q1n1/cff/whjz76qNx6663HpWwITH3PmDFD7r33XhO6dTz/4sWLze88al596/Aw/YNchwii5te3nmBJS0uThg0byo8//mj+Vt+0aZO89957x72ssLe+f/31V3P9yCOPmCFi7du3l1dffVW6d+9uhou1aNFCqhNCdyX0P+Lvv//etHq88847MmjQIDOeV4O3ju0dMmSITJs2zbRwX3vttXL66adXzzEEqNRvv/0md911l/mjS3ssoGajvp3Fn/rOyckxZ9P1N17/E0fNre/rr79eevbsaU6U6x9rOhmqzs/Cb0LNqu8PPvjAzL2zdu3agJQPx//77XnCtG3bttKgQQMTwnQCZJ1AETWnvksPzaOlk2PedNNN5t8dOnSQpUuXyssvvyyTJ0+WaiXQ/duDSffu3V233nprmW179+51j/esV6+ea8qUKQEqHY6Wju3Sr0JYWJj7ord1jL7+e8mSJV7H9TZu3Nj11FNPBazcsKe+i4uL3ccypts59Z2Tk+Pq3Lmz+Z3Pz88PdLFxHL7flsLCQldsbKxr9uzZASkz7KvvYcOGuf/tuV/n5dGxoaj53+/c3FxzzCeffBKQMsO++t66dau5/dprr5W539VXX+267rrrXNUNLd1+0DMqOp7bk44ZUXomVccWXHrppQEqHY6WngHVoQSe9IyZjtvWbkk6zi8iIsKcOdOlwpR2Vdq+fbsZfoCaVd9hYWEBKxsCU9/awt27d28zT4e2jNHa6azvtw4R00v5/98R/PWtf6NpK5gnbf2cOnWqWXEGNf/7rT1WlbZ4o2bV90knnWSGEejf5J42b95slgesbgjdFdCJF7TCdLKs/fv3y+zZs+Xzzz83y4sonXilVatWZny3Tqal3R903JB2SUdwiY+PN5NteNIx+7omt7Vdlw8aNWqUJCcnmwmWdF1fDdydOnUKUKlhZ33rWH29bN261dzWH329n/4e6GcANae+NXDrUiM60c7rr79ubutF6e87J2FqVn3rGMC33nrL1LnW7++//26WB9U1uy+66KKAlRv2/Z57mzxNf8t18jzUrPrWLuT697p+l3WbjunWv8113eaKlpJDcH+/R48eLQ8//LCcdtppZkz3rFmz5OeffzbDgqsbQncFtNV64MCBZrxXYmKi+bJq4NYxYErPqmgw37dvnzRp0kTGjh1rvtiomfSsuI7X15ZubQ3RVjGdiAc103PPPSfjx49339b/sK2TbTfeeGMAS4aqphO06Oynqnnz5mX2bdu2zfy+o+bQXgxfffWVWbM5MzPTTIip3+9ly5aZGXABBC+dDHHJkiXm+62TJGpPRf27bdy4cYEuGmwyYsQIM+G1ZjDNZBq+dQx4dRy/H6J9zANdCAAAAAAAaiKm2gYAAAAAwCaEbgAAAAAAbELoBgAAAADAJoRuAAAAAABsQugGAAAAAMAmhG4AAAAAAGxC6AYAAAAAwCaEbgAAAAAAbELoBgAA1cp5550ns2fPrtLHfOSRR6R9+/ZV+piBeu7nnntOLrnkkip7PACAvUJcLpfL5ucAAOCIbrzxRsnKypL333+/zPbPP/9cLrjgAsnMzJTatWsHrHw4Pj744AMZPXq0bNy4UUJDq65tIDc3VwoLC6VOnTqVft7sUP65j9XBgweladOmMmfOHPnb3/5WJY8JALAPLd0AAKDamDZtmtx0001VGrhVXFxclYVeX2m7RnFxcZU/d2RkpFx33XXmvQIAVH+EbgBA0NEWyn/84x9St25dSUhIkG7duskPP/xQaXdebTEPCQkx91V//vmnXHvttXLiiSdKbGystG3bVt58881Kn/ftt9+WZs2aSXR0tAlRV155pezdu9e9/6mnnjKPU6tWLUlNTZU77rjDtHIey2uxaNnLX77//nv3/iZNmsjTTz/tvr106VJzzOWXX+7edv7558uIESPKPG7590pbgD3vo+9TUlJSmV4Gvry/+jqGDRtW5hh9rzQwatm80f2ffvppma7T//vf/w57rfocuk2f0/O59XHPOOMMU59dunSRTZs2eS2z/nvWrFny3//+1/1eWo9VEasc2rqsj62fgTZt2sgXX3xx2HuwYMEC6dixo0RFRcnXX3/t9f16+eWX5dRTTzXHNGjQoMx75ctnQt8j7RWQn59fabkBAIFH6AYABJ2rrrpKMjIyTLhZvXq1nH766dK9e3fZt2+fz49RUFBggtH8+fPlp59+kltvvVVuuOEGWblyZYX3admypbzyyismzC1cuNAEsfvuu8+9X1tntfVx/fr1JtRpgLz33nur7LXMnDlTdu3aVWkZVWlpqdx9992mhfVYjR8/3rTW+ktDo47L1m7Vltdff92c5NAQ6Y0GVA3MrVq1Oqqyjh07Vv71r3/Jd999J+Hh4XLzzTd7Pe6ee+6Rq6++Wi688ELzfupFg7QvtOu7vrdr166Vzp07m/CrJyY83X///fL444+bLvLt2rU77DGeffZZGTp0qPnMrVu3zoTn5s2b+/WZ0JMLWi/ffvutH+8QACAQCN0AgKCiwUxD59y5c03waNGihTz55JOmJfadd97x+XE0/Gn40hbIk046SYYPH25CmLZmV0QDlI6hTUtLk5NPPtm0dpeUlLj3ayuyjj/XVmcNlo899lilj+fra7GCq7Z81q9f31xXRgO/3ueyyy6TY7F582bTIjty5Ei/79uvXz9zra3JFj1hoS3p2hrsTXp6utSrV++ou5ZPnDhRunbtKq1btzbBd9myZebkSnl6MiImJsa0Muv7qRdtgfeFtkj379/fnBjQ8JyYmCgvvfRSmWMmTJggPXv2NL0ikpOTD3sM/VxocL/rrrvM5+jMM89090Dw9TOhJyf0ufU9AwBUb+GBLgAAAP7QbrbaZbv8GFntZvvLL7+4b2sLomdLr2c4tm5PmjTJhOIdO3aYyak0qGqYqcxXX30lffr0kby8PDn33HNNy6plyZIlMnnyZPn5558lJyfHtERq6NNjvT2ur6/FaknVrsZHos81btw4M8P1u+++e9j+GTNmyIsvvui+ra9bQ6o32kp/2223mZMS/tLu19pzQEO7tiqvWbPG9CjQVt2K6OvW+x0tz1Zl7bKttMW4cePGUlW0dduirekajLVF25Nuq4iWZ+fOnabl2htfPxNKTxxofQMAqjdCNwAgqGgg0UDlbQyu57jjU045pUzA0264AwYMcN/+5z//Kf/+97/NOGhrHLa2NmoIrYwGKu1arC2M2lL5wgsvyJgxY0xX84svvliGDBliWly1hVNbLQcPHmwe01vo9vW1/Prrr+ZaZ6w+En1d+tq127O30H399debbtgW7Q7/5ZdfHnacjlXWEwzapd2ztdrfLubak+D33383j6Ot/9pLoCInnHCCmaXek9Xq7bnYSlFRkdf7R0REuP9ttaZrV/vjTT9LFdGgXBlfPxNKu5sfqdcDACDwCN0AgKCi41t3795tWhm1G3dFtLuw5zhZDX6evvnmG9P92griGs60O3VFrb6eoUm7/OpFx+RaoVvH3upjaMu3FRQr61ruz2vRAKz7GzVqVOnj6dhk7fLsOblXedol2fN98db9WQOudn9+8MEHzSRqR0tPZuhJCn2PdHz3//3f/1V6fIcOHcz7ocHbel4rVOpr0/3Kc1K1o6Wfj/K9H3yxYsUKs4640p4MWu/lJ4yrTHx8vKlLnfRNhyIc7WdCW721F4X1ngAAqi/GdAMAqo3s7GwTqDwvW7dudXcX11bAHj16mC6+OsP2okWLTAuzjt3V1ludQMtXGpoXL15s7qvdg7Ub9Z49eyq9j85cvWrVKtm+fbsJTdqF2wo9GmS1BfaZZ54xLdOvvfaa2V8ZX16LvgfTp083k2tpGNOLNWO6djv3DI563BVXXHHMQUxfm9aFTvZVEQ3mGvqsi9VDwHPiNKu1WycV0+O1bJXRcmtrt54Q8TzJ0alTJ/fEZHpCQbvPHysNtD/++KOZFO+PP/6osPW8PH2P582bZ4YQ6PujJwgqmrCtIjqbuZ6c0V4GW7ZsMV3v9XOjfP18ay8E7fav48YBANUboRsAUG1ol1oNXp6XW265xezT1kUNHdpt+OOPPza3dT1nnYjqmmuucU/C5SsNbtqq2Lt3b7OUlk6m5blUljca+nQSLQ3sAwcOlHPOOccdlk477TSzZNgTTzxhlpJ64403zPjuyvjyWvQ90FZe7Tau3Y71ctZZZ7kD2m+//eZ+PG1p167tx+rAgQMm5Hp21y5PA6sGYuui76PS99GTLsumrbZ6faTx2mFhYeZ90PfOk44L11ZlnW1ehwDoRGTHSj9X2g1fW+K1Nd0z6FdG3xe9aH3r8AEdwqAnCvwxaNAgM6xBx9frsmE6LEHDt/L1863L21nfDQBA9Rbi8hwkBQBANaUtkzr7tQZkJ9EQVtF/1Tpe+v3336+0G/LxpuOOrbW6lbbUamus9hDQkxxHoi35GkS19bey8d/Hm74OHVOv4/nLr7l9vOmSdDo+XodD6HABAED1Rks3ACAo6Fjrqlh3OthU1nqvLazaOlydWK3Z2l1bA7T2KNDu4b4EbqulXJfg0i788E57Prz66qsEbgAIErR0AwAAW4YK6ERh2j1a15fWSdWqM10+Ti/e6NrsOkFddWnpBgAEF0I3AABwPF1+Sy/e6Jj1E0888biXCQBQMxC6AQAAAACwCWO6AQAAAACwCaEbAAAAAACbELoBAAAAALAJoRsAAAAAAJsQugEAAAAAsAmhGwAAAAAAmxC6AQAAAACwCaEbAAAAAACxx/8DJ1pASEXPdlQAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ @@ -67,64 +65,13 @@ "import numpy as np\n", "import pandas as pd\n", "\n", - "df = pd.read_csv('../../static/retail_price.csv')\n", - "product_data = df[df['product_id'] == 'bed1']\n", + "df = pd.read_csv('../static/retail_price.csv')\n", + "product_data = df[df[\"product_id\"] == \"bed1\"].copy()\n", "\n", - "product_data['revenue'] = product_data['unit_price'] * product_data['qty']\n", + "product_data[\"cost\"] = product_data[\"unit_price\"] + product_data[\"freight_price\"]\n", + "product_data[\"profit\"] = product_data[\"total_price\"] - product_data[\"cost\"] * product_data[\"qty\"]\n", "\n", - "plt.figure(figsize=(10, 6))\n", - "plt.scatter(product_data['unit_price'], product_data['revenue'], alpha=0.7)\n", - "plt.title('Зависимость выручки от цены для товара bed1')\n", - "plt.xlabel('Цена за единицу (unit_price)')\n", - "plt.ylabel('Выручка (unit_price × qty)')\n", - "plt.grid(True)\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "fdedd9b0", - "metadata": {}, - "source": [ - "Из графика видно, что зависимость выручки от цены является нелинейной, что делает задачу подходящей для решения с использованием генетического алгоритма. Такой метод хорошо справляется с поиском экстремумов в сложных и нелинейных функциях.\n", - "\n", - "### 2. Структура хромосомы и тип данных гена\n", - "\n", - "На этом этапе мы описываем, как будет представлено одно возможное решение задачи в рамках генетического алгоритма.\n", - "Это важно для понимания, что именно подвергается эволюционным операциям — мутациям и скрещиванию.\n", - "\n", - "**Хромосома** — это предполагаемое решение, то есть конкретная цена товара.\n", - "**Ген** — отдельное значение внутри хромосомы, в нашем случае — одна цена.\n", - "Поскольку задача оптимизируется по одному параметру, каждая хромосома содержит всего один ген.\n", - "\n", - "**Тип данных гена:** `float`.\n", - "\n", - "Таким образом, каждая хромосома представляет собой одно вещественное число — цену товара.\n", - "\n", - "**Обоснование:**\n", - "Минимально необходимая структура, точно соответствующая задаче поиска оптимального значения одной переменной.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "22fe0bbd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Пример хромосомы: [Цена] ∈ (39.24, 45.95)\n" - ] - } - ], - "source": [ - "min_price = product_data['unit_price'].min()\n", - "max_price = product_data['unit_price'].max()\n", - "\n", - "print(f\"Пример хромосомы: [Цена] ∈ ({min_price:.2f}, {max_price:.2f})\")\n" + "print(product_data[[\"lag_price\", \"cost\", \"qty\", \"profit\"]].head())\n" ] }, { @@ -132,62 +79,57 @@ "id": "be42a431", "metadata": {}, "source": [ - "Весь диапазон, в котором работает генетический алгоритм, ограничен значениями от 39.24 до 45.95.\n", - "Алгоритм будет искать наилучшее значение цены в этих пределах, чтобы достичь максимальной выручки.\n", + "## 3. Структура хромосомы\n", "\n", - "### 3. Генерация начальной популяции\n", + "Хромосома — возможная цена продажи.\n", "\n", - "Этот шаг запускает процесс эволюции.\n", - "Формируется начальный набор возможных решений — популяция, с которой начнёт работу алгоритм.\n", - "Каждое решение (индивид) — это случайным образом выбранная цена товара, находящаяся в допустимых пределах, определённых ранее.\n", - "\n", - "**Диапазон допустимых цен:**\n", - "- Нижняя граница: `≈ {min_price:.2f}`\n", - "- Верхняя граница: `≈ {max_price:.2f}`\n", - "\n", - "**Количество особей в популяции:** 10\n", - "\n", - "**Обоснование:**\n", - "- Популяция начальных решений создаёт разнообразие, необходимое для качественного поиска.\n", - "- Границы диапазона исключают нереалистичные значения и сужают пространство поиска, что ускоряет процесс оптимизации.\n", - "\n", - "Пример сгенерированных значений цен:\n" + "Ген — число с плавающей точкой (тип `float`) в диапазоне от 50 до 250 (на основе наблюдаемых цен конкурентов).\n" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "id": "a0fbbea3", "metadata": {}, + "outputs": [], + "source": [ + "price_min = 50\n", + "price_max = 250\n" + ] + }, + { + "cell_type": "markdown", + "id": "c64b3159", + "metadata": {}, + "source": [ + "## 4. Генерация начальной популяции\n", + "\n", + "Популяция — набор возможных цен.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "28313ddf", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Начальная популяция (цены):\n", - "[[45.67003515]\n", - " [40.87201011]\n", - " [40.05529638]\n", - " [41.93519615]\n", - " [40.91820001]\n", - " [42.33300812]\n", - " [39.46643525]\n", - " [43.37890575]\n", - " [45.88634421]\n", - " [44.99696928]]\n" + "Пример популяции: [ 50.41371951 50.48087187 148.54284113 106.27111405 179.03501848\n", + " 134.91448339 113.71686216 140.57834704 106.41741383 193.22289117]\n" ] } ], "source": [ + "import numpy as np\n", "\n", - "def generate_initial_population(pop_size, price_range):\n", - " return np.random.uniform(price_range[0], price_range[1], (pop_size, 1))\n", + "def generate_population(pop_size, price_min=50, price_max=250):\n", + " return np.random.uniform(price_min, price_max, size=pop_size)\n", "\n", - "population_size = 10\n", - "initial_population = generate_initial_population(population_size, (min_price, max_price))\n", - "\n", - "print(\"Начальная популяция (цены):\")\n", - "print(initial_population)\n" + "population = generate_population(10)\n", + "print(\"Пример популяции:\", population)\n" ] }, { @@ -195,34 +137,14 @@ "id": "a05d507e", "metadata": {}, "source": [ - "Мы сгенерировали 10 начальных особей — потенциальных вариантов оптимальной цены.\n", - "Каждая строка соответствует отдельной особи, представленной одной переменной — ценой товара.\n", - "Значения равномерно распределены в пределах ≈ (39.24, 45.95), что соответствует заданному диапазону.\n", + "## 5. Фитнес-функция\n", "\n", - "### 5. Фитнес-функция\n", - "\n", - "Фитнес-функция — это функция оценки, определяющая, насколько хорошо конкретное решение (цена) справляется с задачей максимизации выручки.\n", - "Целевая метрика — прогнозируемая выручка, которая рассчитывается по формуле: **выручка = цена × спрос**.\n", - "\n", - "- Цена — это значение, генерируемое генетическим алгоритмом (значение хромосомы).\n", - "- Спрос — прогноз, полученный с помощью линейной регрессии, обученной на исторических данных.\n", - "\n", - "Таким образом, спрос моделируется простой линейной регрессией, построенной на предыдущих наблюдениях.\n", - "\n", - "**Примеры расчётов:**\n", - "- Цена: 42.84 → Прогноз спроса: ≈ 7.99 → Выручка: ≈ 342\n", - "- Цена: 40.29 → Прогноз спроса: ≈ 11.84 → Выручка: ≈ 477\n", - "\n", - "Это означает, что в рамках текущей модели наибольшую выручку обеспечивает цена около **40.29**.\n", - "\n", - "**Обоснование:**\n", - "Позволяет оценивать каждую особь без необходимости в точной аналитической функции спроса.\n", - "Интерпретируется интуитивно: выручка равна произведению цены и объёма продаж.\n" + "Фитнес — средняя прибыль при заданной цене:" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 6, "id": "14ba56f8", "metadata": {}, "outputs": [ @@ -230,24 +152,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "Цена: 42.0, Предсказанная выручка: 388.87\n" + "Фитнес для 100.0: 396.354375006875\n" ] } ], "source": [ - "from sklearn.linear_model import LinearRegression\n", + "def fitness(price, cost, qty):\n", + " profit = (price - cost) * qty\n", + " return profit.mean()\n", "\n", - "X = product_data[['unit_price']].values\n", - "y = product_data['qty'].values\n", - "reg = LinearRegression().fit(X, y)\n", + "cost = product_data[\"cost\"].values\n", + "qty = product_data[\"qty\"].values\n", "\n", - "def fitness_function(price_value):\n", - " predicted_qty = reg.predict(np.array([[price_value]]))[0]\n", - " revenue = price_value * max(predicted_qty, 0)\n", - " return revenue\n", - "\n", - "test_price = 42.0\n", - "print(f\"Цена: {test_price}, Предсказанная выручка: {fitness_function(test_price):.2f}\")\n" + "print(\"Фитнес для 100.0:\", fitness(100.0, cost, qty))\n" ] }, { @@ -255,36 +172,14 @@ "id": "843f11f8", "metadata": {}, "source": [ - "Если установить цену на уровне 42.00, модель прогнозирует соответствующий спрос, при котором ожидаемая выручка составит 388.87.\n", - "Это позволяет сравнивать различные ценовые значения между собой и выбирать ту цену, при которой выручка будет максимальной.\n", + "## 6. Оператор кроссинговера\n", "\n", - "### 6. Оператор кроссинговера\n", - "\n", - "**Кроссинговер (скрещивание)** — это процесс обмена «генетической информацией» между двумя родительскими решениями с целью генерации нового потомка.\n", - "Он играет важную роль в расширении пространства поиска и способствует появлению новых потенциально успешных комбинаций параметров.\n", - "\n", - "В данной задаче для скрещивания мы используем **усреднение двух цен**.\n", - "Это оправдано, так как цена — непрерывная величина, и среднее значение даёт логичный результат между двумя родителями.\n", - "\n", - "**Конкретно в нашем случае:**\n", - "- Каждое решение — это одна цена.\n", - "- Родители: два числовых значения.\n", - "- Потомок: арифметическое среднее между этими значениями.\n", - "\n", - "**Пример:**\n", - "- Родитель 1: 45.35\n", - "- Родитель 2: 44.23\n", - "- Потомок: (45.35 + 44.23) / 2 = **44.79**\n", - "\n", - "Такой способ скрещивания позволяет плавно и последовательно исследовать возможные варианты в рамках допустимого диапазона.\n", - "\n", - "**Обоснование:**\n", - "Поскольку цена — непрерывный параметр, использование среднего значения является естественным и эффективным способом комбинирования родительских решений.\n" + "Простой арифметический кроссовер:\n" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 9, "id": "42170c5c", "metadata": {}, "outputs": [ @@ -292,20 +187,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Родитель 1: 41.5, Родитель 2: 43.0, Потомок: 42.25\n" + "Пример кроссовера: 140.17469125275707\n" ] } ], "source": [ + "def crossover(parent1, parent2):\n", + " alpha = np.random.rand()\n", + " child = alpha * parent1 + (1 - alpha) * parent2\n", + " return child\n", "\n", - "def crossover_operator(parent1, parent2):\n", - " return np.array([(parent1[0] + parent2[0]) / 2.0])\n", - "\n", - "p1 = np.array([41.5])\n", - "p2 = np.array([43.0])\n", - "offspring = crossover_operator(p1, p2)\n", - "\n", - "print(f\"Родитель 1: {p1[0]}, Родитель 2: {p2[0]}, Потомок: {offspring[0]}\")\n" + "print(\"Пример кроссовера:\", crossover(100, 150))\n" ] }, { @@ -323,32 +215,15 @@ "id": "7e94e0c9", "metadata": {}, "source": [ - "### 7. Операторы мутации\n", + "## 7. Операторы мутации\n", "\n", - "Мутация — это механизм случайных изменений в хромосомах (решениях), который служит для:\n", - "- поддержания разнообразия в популяции,\n", - "- предотвращения преждевременной стабилизации на одном решении,\n", - "- возможности выхода из локального максимума.\n", - "\n", - "В нашем случае каждая хромосома представляет собой одно значение — цену.\n", - "Мы реализуем два типа мутаций:\n", - "\n", - "1. **Локальное случайное изменение** (`mutation_func_small`):\n", - " - Вносит небольшое случайное смещение в пределах от -0.5 до +0.5.\n", - " - Эффективно для точечной настройки и локального поиска.\n", - " - **Обоснование:** снижает риск ранней сходимости, позволяя изучать близкие значения.\n", - "\n", - "2. **Случайный скачок по диапазону** (`mutation_func_jump`):\n", - " - Заменяет текущую цену на случайное значение из всего допустимого диапазона.\n", - " - Полезен для глобального поиска и выхода из локальных экстремумов.\n", - " - **Обоснование:** обеспечивает баланс между улучшением текущих решений и поиском новых направлений.\n", - "\n", - "Комбинирование этих двух операторов позволяет гибко управлять соотношением между **эксплуатацией** (углублённая работа с хорошими решениями) и **исследованием** (поиск новых областей в пространстве решений).\n" + "- Мутация 1: добавление случайного смещения.\n", + "- Мутация 2: замена на случайное значение.\n" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 10, "id": "b16fd3c9", "metadata": {}, "outputs": [ @@ -356,22 +231,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Оригинал: 42.0, Мелкая мутация: 41.87, Прыжок: 44.98\n" + "Мутация 1: 100.00798577997959\n", + "Мутация 2: 123.77219890881597\n" ] } ], "source": [ - "def mutation_operator_small(price_value):\n", - " return price_value + np.random.uniform(-0.5, 0.5)\n", + "def mutation_1(price):\n", + " return price + np.random.uniform(-5, 5)\n", "\n", - "def mutation_operator_jump():\n", - " return np.random.uniform(min_price, max_price)\n", + "def mutation_2(price, price_min=50, price_max=250):\n", + " return np.random.uniform(price_min, price_max)\n", "\n", - "original = 42.0\n", - "mutated_small = mutation_operator_small(original)\n", - "mutated_jump = mutation_operator_jump()\n", - "\n", - "print(f\"Оригинал: {original}, Мелкая мутация: {mutated_small:.2f}, Прыжок: {mutated_jump:.2f}\")\n" + "print(\"Мутация 1:\", mutation_1(100.0))\n", + "print(\"Мутация 2:\", mutation_2(100.0))\n" ] }, { @@ -379,115 +252,40 @@ "id": "a803c415", "metadata": {}, "source": [ - "Исходная цена: 42.00 \n", - "После применения малой мутации → 41.87: незначительное изменение — хорошо подходит для уточнения результата.\n", - "После случайного скачка → 44.98: новое значение — помогает выйти из локального застоя.\n", + "## 8. Генетический алгоритм\n", "\n", - "### 8. Реализация базового генетического алгоритма\n", - "\n", - "**Генетический алгоритм (ГА)** — это метод поиска оптимума, вдохновлённый механизмами естественного отбора.\n", - "Популяция решений проходит через отбор, кроссинговер и мутации, постепенно приближаясь к наилучшему решению.\n", - "\n", - "Так как точная зависимость между ценой и выручкой неизвестна и, скорее всего, является сложной и нелинейной, мы используем генетический алгоритм — метод, не требующий аналитического выражения функции.\n", - "\n", - "1. Сначала обучается модель линейной регрессии, которая предсказывает спрос в зависимости от цены: D(x), где x — это цена.\n", - "2. Для каждой предложенной алгоритмом цены выполняются шаги:\n", - " - прогнозируется значение спроса;\n", - " - вычисляется соответствующая выручка;\n", - " - результат используется как значение фитнес-функции.\n", - "3. Из двух родительских решений формируется новое — путём усреднения цен.\n", - "4. Затем применяется мутация — небольшое случайное изменение цены, чтобы сохранить разнообразие в популяции.\n", - "\n", - "Процесс запускает эволюцию: из текущих решений отбираются лучшие, формируются потомки, применяется мутация.\n", - "Эти шаги повторяются в течение 50 поколений.\n" + "Реализация основного ГА:\n" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 11, "id": "f62d5f8d", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "c:\\Users\\Natalia\\Desktop\\6 семестр\\МИИ\\AIM-PIbd-32-Katysheva-N-E\\aimvenv\\Lib\\site-packages\\pygad\\visualize\\plot.py:120: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.\n", - " matplt.legend()\n" - ] - }, - { - "data": { - "image/png": "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x96/H1xaJu4fTZldfshaBCDkiACkf1m7v0ifffZZT0DQqur169d7ttemBF2uvxS96S8sXb5t2zbPMm1i0mXffPNNivf96aefUvwCT17roLp37272sW7duhS/kJNfCK8mAGnNgW732WefOZkhPef+xRdfmNe+9NJLfvfds2dPUyNx8uRJzzK3pi75BUKb1vSv8SZNmvgEAL2ItmvXzu/+33nnHbO/RYsWpQgsX3755VWVg4YlfZ3WvqQnAGlQ0c+i1i5obUJyt99+u3mN1lImv+B//PHHKbZ31+kF90q0llDfu2HDhj7L//e//5naGA2FLvcCfv/99zuZIbWmLH3o/5X0BqDM+gy5Acjf/1lvmV1+yFqMAkOOoEO1x48fb77XYdrly5c3w+Cff/55adiwoWe7hx9+2CybOnWqDBgwwCw7duyYLF68WFq2bCn16tVL0/v9/PPPnvdz9evXT+6++27P89jYWFmyZInUrFnT7Nulw9mffPJJmTFjhrz88stmVExm0dFj06dP91mmw+qHDx9+zfu80rnrCDy1a9cuM0osOS2XS5cuyW+//SbNmjXzLNeh/kWKFPHZNjg42Pws4+PjPct0GoGkpCRJTEz0u//du3ebrzt37pQ777zTZ93NN9/s95z0+qlTDGhZbd26VU6ePGmO0XXkyBFJDz2Wc+fOSdu2bf2ObtLlUVFRpmxbt27ts65p06Yptq9cubL56l0uqSlevLjcddddMnfuXPnll1+kUaNGZvm8efNMGT700EOebevWrSthYWFmBOXhw4fNz1RHG+oUCHnyZMysKCEhIaYsMkNmfYZ0yoeKFStKZGSkKUNd1qZNG1Ne3tMhZEX5IesQgJAjdOrUSZYuXXrF7fTi37t3bxM+9EKnQ831oqfDvAcOHOizrf7STO3ip3OS6MO94Ldo0SLFNvoeul/vC4zSYeTdu3eXOXPmmGPu2rXrVZ+ve0w6H8+VAlDysBIaGnrFAJSec9ch8mrmzJmXfY/k8xlpufijFzC9WCXfvw6910da9+99Xsk99dRT8u6775r5ozQs6MVOL9RKy08vlOmhUxlc7v31/by3u1K5aJko73K5HP0MagD69NNPPQFIp1LQ8K1/KHjvd/ny5SYUfP755/LMM894PmdDhw6VF154IVMDe3pl1mdIQ6R+1nUKikWLFsnXX39tluvnRf+geuKJ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6xCAAACAdQhAAADAOgQgAABgHQIQAACwDgEIAABYhwAEAACsQwACAADWIQABAADrBHQAioyMlKCgIBk+fLh5vn//fvPc32PevHme1x08eFC6du0qhQoVknLlysnIkSPl4sWL2XgmAAAgkARLgIqJiZEpU6ZIWFiYZ1mVKlXk6NGjPtt98MEH8tprr0mXLl3M86SkJBN+KlSoIOvXrzfbP/zww5IvXz559dVXs/w8AABA4AnIGqCEhATp27evTJ06VUqWLOlZnjdvXhNsvB8LFiyQ3r17S5EiRcw23333nWzfvl0+/fRTady4sQlGL7/8skyaNEnOnz+fjWcFAAACRUAGoCFDhphanA4dOlx2u02bNsnPP/8sAwYM8CyLjo6Whg0bSvny5T3LOnXqJKdOnZJt27Zl6nEDAICcIeCawObMmSObN282TWBX8tFHH0ndunWlZcuWnmWxsbE+4Ue5z3VdahITE83DpYEJAADkTgFVA3To0CEZNmyYzJw5UwoUKHDZbf/66y+ZNWuWT+1PekREREjx4sU9D+1vBAAAcqeACkDapBUXFydNmjSR4OBg81i1apW888475nvt4OyaP3++nD171nRw9qb9go4dO+azzH2u61IzevRoOXnypOehYQwAAOROAdUE1r59e9myZYvPsv79+0udOnVk1KhRphO0d/PXXXfdJWXLlvXZvkWLFvLPf/7TBCkdAq+ioqKkWLFiUq9evVTfOyQkxDwAAEDuF1ABqGjRotKgQQOfZYULF5bSpUv7LN+zZ4+sXr1avv766xT76Nixowk6Dz30kEyYMMH0+xkzZozpWE3AAQAAAdcEllYff/yxVK5c2YSd5LSWaPHixear1gY9+OCDppnspZdeypZjBQAAgSfIcRwnuw8iEOkoMO0Mrf2BtPkMAADknut3jqwBAgAASA8CEAAAsA4BCAAAWIcABAAArEMAAgAA1iEAAQAA6xCAAACAdQhAAADAOgQgAABgHQIQAACwDgEIAABYhwAEAACsQwACAADWIQABAADrEIAAAIB1CEAAAMA6BCAAAGAdAhAAALAOAQgAAFiHAAQAAKxDAAIAANYhAAEAAOsQgAAAgHUIQAAAwDoEIAAAYB0CEAAAsA4BCAAAWIcABAAArEMAAgAA1iEAAQAA6xCAAACAdQhAAADAOgQgAABgHQIQAACwDgEIAABY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o0bJX3/9lS3HGGj0Ttd16tTJ7sMAkAqawABc1m+//SavvfaalC1bVpYuXeo3/CgNR1FRUSmahH799Vfp06ePqQXRJrPQ0FB58skn5c8//0y1z8yePXukR48eUrJkSVOr1KFDB/nll1/8vq/WQI0YMcIcV0hIiJQpU0Z69eolW7duTbXJKj4+XoYOHSpVqlSR4OBgmT59ulm/adMms7xBgwam5qZgwYLSsGFDiYyMNDU6yY/1wIED5qHfu49x48alOJ/k9DUDBgwwNWVaJpUrVzbPDx48mGLb2267zexH31/3rcev51mrVi157733UvmpAbgSaoAAXNaMGTMkKSlJBg8ebELQlWigcH311VfSu3dvyZMnj3Tv3t0Eju3bt8u7774r3377rWzcuNGEHG8aHJo3by7169eXRx991DQjffnll9K2bVvZsWOHlC9f3rOtrtOAcPjwYenYsaPcfffdJhB9/vnnZv/Lli2T8PBwn/0nJiZKu3btJCEhQe666y5zvO4+p06dKosWLTJNfHfccYecPXtWVq5cKaNHj5aYmBizX1WiRAkZO3asvPXWW+b58OHDPfvX47lSoGzVqpXpL9WtWzdznhrWPv74Y/Pea9euNeEmufvvv19++OEH6dKli2mGnDt3rgwZMsTUNA0cOPCKPxcAyWTZfecB5Eht27bVfoLOsmXLrup1x48fd4oVK+Zcd911zv79+33WzZ492+xz6NChnmX79u0zy/QRGRnps/2YMWPM8oiICJ/lLVu2dPLmzessXbrUZ/muXbucokWLOg0bNvRZHhoaavbTqVMn5+zZsymO+cCBA87Fixd9ll26dMl59NFHzevWrl2bYn/68Mc9n379+vktzylTpvgsnzRpklnerl07n+Vt2rQxy8PDw52TJ096lu/cudMJDg52ateu7ff9AVweAQjAZdWtW9dcgHfs2JFi3YoVK5yxY8f6PHSZeuONN8zrPvnkE7/7bdKkiVOmTJkUgaF69epOUlKSz7buup49e3qWbd682SzTcOLP008/bdZv2bIlRQD65ZdfrqoMNm3aZF43bty4dAUgDVi6rF69eiZYedNzrlOnjll/8ODBFAFo+fLlKd7DXXfq1KmrOh8AjkMTGIBrps1D48ePT7Fcm4E2bNhgvtdmLm2qSu7cuXNy/Phx89B+O67GjRubJjNv2kdGad8dl7v/Y8eOefrdeNu5c6fnq/bpcRUoUMD06/Hn/Pnzpnluzpw55nXaTOY9UPbIkSOXLY+0TCOg2rRpY/r1eNNz1qY3fV/dTpsLvTVt2jTF/rzLpWjRouk6NsA2BCAAl6X9Y7TvjV78k49q0uDhhg8NDdpPxXXixAnzddKkSZfd/5kzZ3wCkM4vlFq/Iu2LlHz/S5YsMY/L7d9buXLlUoQP1z333GP64WgfnPvuu89sq31sNGC8/fbbpv9Qepw6dcp89e7H5M0dLu9u5y2t5QIgbQhAAC6rZcuWpqZnxYoVpvNwWrkX7C1btvjUwGQUd/8TJ040I7fSKrXwo52cNfx06tTJBCrtaOxd26QBKKOOWWut/ImNjfXZDkDmYRg8gMvq16+faZ754IMPTHNVWrmjr3SSxMyQ0ft3m+m6du3qE37UmjVr/L5Gt7ua2hdt3lOrV6/2aVpT+lyXe28HIPMQgABcljYH6SzQOrxch2DrHD3+ePfPUf379zf9UnQSRZ0lOjkdYu7247kWN998swlBs2fPNpMz+pu9etWqVWnen85PpHQYujc99oiICL+vKVWqlAmF2p8pLapWrWqG8+s+ddi7Nw2Y2tSotWzJ+/8AyHg0gQG4on/+85+mg/Abb7xh+gFpZ91GjRpJoUKFTDDSyQ51jpoiRYp4ai90ziANJ/fee6/ZtnPnzua12o9G5/rRcKLNazq54rXS/Wug0IkWdU4enYxRJy/UCQW1Zkjn2klrONFApQ+dX+fo0aNmLiLdj85lpLVC8+fPT/EaDSs//vijCYatW7c2kxpq2egjNe+//76ZB0jn7tEmN72vmgYifR8tM10PIAtk9zA0ADmHDj0fNGiQGa5dpEgRJ1++fE758uXN3DWvvfaac+zYsRSv0flqBgwYYIaL58+f3ylZsqSZn+epp55yfvjhhyvOm+PSdTrsO7kTJ06YeYIaNGjgFCxY0BzXDTfc4DzwwAPOF198keZh6youLs4Mq69UqZJToEABc5w6P8/vv//u99hOnz7tDBw40KlYsaKZj0i30akArnQ+Oi9S//79zet0Lh/9qs+Tz5fkPdTdH923rtP3AnB1uBkqAACwDn2AAACAdQhAAADAOgQgAABgHQIQAACwDgEIAABYhwAEAACsQwACAADWIQABAADrEIAAAIB1CEAAAMA6BCAAAGAdAhAAALAOAQgAAIht/h+I06nkAjBsJwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "💡 Оптимальная цена: 40.14\n", - "📈 Максимальная предсказанная выручка: 484.84\n" - ] - } - ], + "outputs": [], "source": [ - "from sklearn.linear_model import LinearRegression\n", - "import pygad\n", + "def genetic_algorithm(cost, qty, generations=20, pop_size=10):\n", + " population = generate_population(pop_size)\n", + " best_scores = []\n", "\n", - "min_price = product_data['unit_price'].min()\n", - "max_price = product_data['unit_price'].max()\n", + " for gen in range(generations):\n", + " scores = [fitness(p, cost, qty) for p in population]\n", + " best_scores.append(max(scores))\n", + " parents = sorted(zip(scores, population), reverse=True)[:2]\n", + " new_population = []\n", "\n", - "X = product_data[['unit_price']].values\n", - "y = product_data['qty'].values\n", - "reg = LinearRegression()\n", - "reg.fit(X, y)\n", + " for _ in range(pop_size):\n", + " child = crossover(parents[0][1], parents[1][1])\n", + " if np.random.rand() < 0.5:\n", + " child = mutation_1(child)\n", + " else:\n", + " child = mutation_2(child)\n", + " child = np.clip(child, price_min, price_max)\n", + " new_population.append(child)\n", "\n", - "def fitness_func(ga_instance, sol, sol_idx):\n", - " price = sol[0]\n", - " predicted_qty = reg.predict(np.array([[price]]))[0]\n", - " return price * max(predicted_qty, 0)\n", + " population = new_population\n", "\n", - "def crossover_func(parents, offspring_size, ga_instance):\n", - " offspring = np.empty(offspring_size)\n", - " for k in range(offspring_size[0]):\n", - " parent1_idx = k % parents.shape[0]\n", - " parent2_idx = (k + 1) % parents.shape[0]\n", - " offspring[k, 0] = (parents[parent1_idx, 0] + parents[parent2_idx, 0]) / 2.0\n", - " return offspring\n", - "\n", - "def mutation_func(offspring, ga_instance):\n", - " for idx in range(offspring.shape[0]):\n", - " if np.random.rand() < 0.5:\n", - " offset = np.random.uniform(-0.5, 0.5)\n", - " offspring[idx, 0] += offset\n", - " return offspring\n", - "\n", - "ga_instance = pygad.GA(\n", - " num_generations=50,\n", - " num_parents_mating=4,\n", - " fitness_func=fitness_func,\n", - " sol_per_pop=10,\n", - " num_genes=1,\n", - " gene_space={'low': float(min_price), 'high': float(max_price)},\n", - " parent_selection_type=\"rank\",\n", - " keep_parents=2,\n", - " crossover_type=crossover_func,\n", - " mutation_type=mutation_func,\n", - " mutation_probability=0.2\n", - ")\n", - "\n", - "ga_instance.run()\n", - "ga_instance.plot_fitness()\n", - "\n", - "best_solution, best_fitness, _ = ga_instance.best_solution()\n", - "print(f\"💡 Оптимальная цена: {best_solution[0]:.2f}\")\n", - "print(f\"📈 Максимальная предсказанная выручка: {best_fitness:.2f}\")\n", - "\n" + " return population, best_scores\n" ] }, { @@ -495,119 +293,38 @@ "id": "76941121", "metadata": {}, "source": [ - "На графике показано, как менялось значение лучшего решения (fitness) на протяжении поколений.\n", + "## 9. Влияние параметров на сходимость\n", "\n", - "Замечаем горизонтальную линию — это означает, что уже в первом поколении было найдено хорошее решение, которое затем не улучшалось. Такая ситуация может быть вызвана:\n", - "- недостаточным разнообразием стартовой популяции;\n", - "- слишком низкой вероятностью мутации;\n", - "- почти линейной зависимостью в модели регрессии с явно выраженным максимумом.\n", - "\n", - "При цене 40.14 модель предсказывает максимальную выручку в размере 484.84.\n", - "Если модель предсказания достаточно точна, именно эту цену можно использовать для увеличения прибыли.\n", - "\n", - "### 9. Эксперименты с параметрами\n", - "\n", - "Мы проводим серию экспериментов по варьированию параметров генетического алгоритма. Это важно для оценки устойчивости модели и эффективности её настройки.\n", - "\n", - "В эволюционных алгоритмах параметры сильно влияют на итоговый результат, поэтому важно понимать их роль:\n", - "- **Размер популяции** влияет на охват пространства решений. Чем он больше — тем выше разнообразие, но дольше время выполнения.\n", - "- **Размер брачного пула** определяет степень селективности. Малый пул может ускорить сходимость, но повышает риск локального застревания. Большой — повышает вариативность, но снижает скорость.\n", - "- **Вероятность мутации** регулирует уровень случайности. Слишком низкое значение ведёт к застою, слишком высокое — к хаотичному поиску.\n", - "\n", - "Такой эксперимент помогает понять:\n", - "- При каких настройках достигается максимальный результат?\n", - "- Какие комбинации обеспечивают оптимальное соотношение качества и времени выполнения?\n", - "\n", - "Мы многократно запускаем генетический алгоритм, перебирая разные значения трёх ключевых параметров:\n", - "- `pop_size` — число особей в популяции,\n", - "- `mating_pool` — количество родителей для скрещивания,\n", - "- `mutation_prob` — вероятность мутации.\n", - "\n", - "Фиксируем следующие показатели:\n", - "- `best_fitness` — наилучшую достигнутую выручку,\n", - "- `time_sec` — затраченное время (в секундах).\n", - "\n", - "Все результаты записываются в датафрейс `df_results`.\n", - "\n", - "**Обоснование:**\n", - "Были протестированы различные конфигурации параметров с целью найти наиболее эффективные настройки с точки зрения производительности и точности (см. пункт 9).\n" + "Визуализируем, как меняется фитнес по поколениям.\n" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 12, "id": "3b4aa232", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - " pop_size mating_pool mutation_prob best_fitness time_sec\n", - "0 10 2 0.1 523.703232 0.02\n", - "1 10 2 0.2 508.416818 0.02\n", - "2 10 2 0.4 509.445109 0.02\n", - "3 10 4 0.1 450.753100 0.02\n", - "4 10 4 0.2 470.501994 0.02\n", - "5 10 4 0.4 503.517669 0.02\n", - "6 10 6 0.1 526.646992 0.02\n", - "7 10 6 0.2 516.680176 0.02\n", - "8 10 6 0.4 515.247424 0.02\n", - "9 20 2 0.1 526.962671 0.03\n", - "10 20 2 0.2 527.295935 0.03\n", - "11 20 2 0.4 514.346601 0.03\n", - "12 20 4 0.1 520.256511 0.03\n", - "13 20 4 0.2 504.030835 0.03\n", - "14 20 4 0.4 509.056689 0.03\n", - "15 20 6 0.1 523.256770 0.03\n", - "16 20 6 0.2 524.230935 0.03\n", - "17 20 6 0.4 524.004429 0.03\n", - "18 50 2 0.1 523.806662 0.07\n", - "19 50 2 0.2 527.310583 0.07\n", - "20 50 2 0.4 524.661809 0.07\n", - "21 50 4 0.1 526.488434 0.08\n", - "22 50 4 0.2 521.091870 0.08\n", - "23 50 4 0.4 509.458550 0.08\n", - "24 50 6 0.1 520.995407 0.08\n", - "25 50 6 0.2 517.040176 0.09\n", - "26 50 6 0.4 510.612420 0.08\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHHCAYAAABeLEexAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAS2ZJREFUeJzt3QucTeX++PHv3JlxT0i5JpIQKlFuCancu5ATJ6JT/FU6Kqdy6XKEbpLT5VfhdBQpl6LjUK5JQomQIqWTWzeGGWbGzP6/vg9rn71n9ozZM2uvvfbsz/v12systWbtNc/es/d3f5/v8zwxHo/HIwAAAFEsNtwXAAAAEG4ERAAAIOoREAEAgKhHQAQAAKIeAREAAIh6BEQAACDqERABAICoR0AEAACiHgERAACIegREAAAg6hEQwXG7d++WO++8U+rWrSulSpWScuXKyZVXXilTpkyR48ePh/vyAABRKD7cF4DosnjxYrnpppskKSlJBgwYIBdffLFkZmbKJ598IqNGjZJt27bJq6++Gu7LBABEmRgWd4VT9uzZI02aNJHzzjtPli9fLuecc47f/l27dpmA6Z577gnbNQIAohNdZnDMpEmT5NixY/L666/nCYZUvXr1vMHQ9OnTJSYmRt544w2/Y/7+97+b7R9++KF3W1pamtx///1So0YNk3lq0KCBPP300xIo1l+5cqX5+UA3X+PGjcuzTa+9WrVqZruex9K+fXuzrWfPnnnuT7sGdZ9mwnwFc83/+te/5PLLL5fk5GSpWLGitG3bVpYuXWr21a5dO9/fR2+6X/3www/me72PYFk/G+im12Y5fPiw3Hvvvd7fSR/PiRMnSk5OTp5zBboObSNtS18ZGRkyduxYcy49p577gQceMNstBf3+erPOaT32vo+duv766812fcwt+jO5r8W69hkzZvht/+abb+TGG2+USpUqmS7gSy+9VN5///08v5+2z3333WceE/1d9IOBZkl//fXXfK9v37595ng9pz7/LIcOHZLBgwdL1apVzX02bdpUZs6cWaTH7fvvvzdZW71+fY5dccUV5oNJYejf6dVXXy1VqlQxv9NFF10kL730UsBj8/vbs56jBR2jN80iW7788kvp2rWr6W4vU6aMdOzYUT777DO/+9PHSX9u9erV5u/wrLPOMsdrm//xxx9+x+o1/PnPf/bbNnfu3IB/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0KFwX1rES0tLM+2pAWcgkyZNkhdeeEFefvllWb9+vaSkpJi21z80BG/VqlXmBeqzzz6TZcuWmUUYO3fubB4Hy3333ScffPCBzJ071xyvS+H07t07rNcdqXTWfH1j3rRpk2zcuFGuvvpq6dGjh2zbts3sp61DY8OGDfLKK6+YYNQX7W2/Ro0ayf79+723Tz75JPTtrcPuER6XX365Z9iwYd7vs7OzPdWrV/dMmDAhrNdV0ujTfP78+d7vc3JyPNWqVfNMnjzZu+3w4cOepKQkz9tvvx2mqyxZDh06ZNp91apV3vZNSEjwzJ0713vMjh07zDHr1q0L45WWHBUrVvS89tprtHWIHD161HPBBRd4li1b5mnXrp3nnnvuMdtpb/uNHTvW07Rp04D7QtneZIjCJDMz03y6064a3/XS9Pt169aF9dpKuj179siBAwf82l7XudEuS9reHkeOHDH/V6pUyfyvz3XNGvm2uabAa9asSZsXU3Z2tsyePdtk47TrjLYODc2AXn/99X7tqmjv0NASBi15qFu3rvTv31/27t0b8vZmcdcw+fXXX80LWdWqVf226/fffPNN2K4rGmgwpAK1vbUPRZeTk2PqK6688kq5+OKLzTZt18TERKlQoYLfsbR50W3dutUEQNrNq3UU8+fPl4suukg2b95MW9tMA04ta9Aus9x4bttPP5zOmDFDGjRoYLrLxo8fL23atJGvv/46pO1NQATA9k/S+sLl2+cP++mbhQY/mo179913ZeDAgaaeAvb66aef5J577jG1cTr4BaHXtWtX79dar6UBUq1ateSdd94xg2BChS6zMKlcubLExcXlqYzX76tVqxa264oGVvvS9vYbPny4LFq0SFasWGEKfy3artpNfPjwYb/jafOi00/J9erVkxYtWphRfjqIYMqUKbS1zbSLRge6NG/eXOLj481NA08dlKFfa2aC9g4tzQbVr19fdu3aFdLnNwFRGF/M9IXs448/9utq0O81DY7QqVOnjvnD8W371NRUM9qMti8arV3XYEi7bZYvX27a2Jc+1xMSEvzaXIfla10AbW4Pff3IyMigrW3WsWNH0z2p2Tjrdumll5q6Futr2ju0jh07Jrt37zbTpIT0+V2skmwUy+zZs83IphkzZni2b9/uGTp0qKdChQqeAwcOhPvSSsSIkC+//NLc9Gn+7LPPmq9//PFHs/+pp54ybb1w4ULPli1bPD169PDUqVPHc/z48XBfekS66667POXLl/esXLnSs3//fu8tPT3de8xf/vIXT82aNT3Lly/3bNy40dOqVStzQ/AeeughM4Jvz5495vmr38fExHiWLl1q9tPWoeU7ykzR3va6//77zWuJPr/Xrl3rueaaazyVK1c2o1dD2d4ERGE2depU88AmJiaaYfifffZZuC+pRFixYoUJhHLfBg4c6B16/+ijj3qqVq1qgtKOHTt6du7cGe7LjliB2lpv06dP9x6jwebdd99thocnJyd7evXqZYImBG/QoEGeWrVqmdeNs88+2zx/rWBI0dbOBkS0t71uueUWzznnnGOe3+eee675fteuXSFv7xj9p/gJLQAAgMhFDREAAIh6BEQAACDqERABAICoR0AEAACiHgERAACIegREAAAg6hEQAQCAqEdABAAAoh4BEQDH/PnPf5aYmJh8b7kXbAQApxAQAXDUtddeK/v37/e7vffee+G+LABRjoAIgKOSkpKkWrVqfrdKlSr5HaMBUqNGjcyxtWvXlmeeecZvv257/vnnvd8/8sgjct5558kPP/xQ6HOocePG5clS9ezZ07tfV4//61//Kueee66kpKRIy5YtZeXKld79M2bMkAoVKvidU69Bz6MroSs9Pnf267bbbjPbFixY4N32008/yc0332zOp+3Ro0cPv98HQGgREAFwlU2bNpnAoG/fvrJ161YTtDz66KMm+AhEA51XXnlFli1bZgKfYM6hSzlq0GRlqvRnfA0fPlzWrVsns2fPli1btshNN91kMlzfffddsX6/999/329bVlaWdOnSRcqWLStr1qyRtWvXSpkyZcx9ZWZmFvm+ABRefBDHAkDIPfvss9KxY0cTwKj69evL9u3bZfLkyaYGyddrr70mjz32mCxfvlwaNmwY9Dk0ECldurTJUin9WrNCau/evTJ9+nTzf/Xq1c02zRYtWbLEbP/73/9epN9v5MiRMmrUKO+1qTlz5khOTo75fTRzpPQ+NFukGabOnTsX6b4AFB4ZIgCusmPHDrnyyiv9tun3mpXJzs72blu4cKHceeedJli5+OKLi3SO1NRU0xUWiGaW9FgNpjRbY91WrVolu3fv9h535MgRv/2accqPdpF9//33cv/99/tt/+qrr2TXrl0mQ2SdR7vNTpw44XdfAEKHDBGAiKTdSppZGTNmjOkSmzBhQtDn2Ldvnzf7k9uxY8ckLi7OdHHp/740YLFoEPPFF194v//555+lffv2ec6n2agHHnhAnnzySZOJyn1fLVq0kFmzZuX5ubPPPjvo3wtA8AiIALiKdn1psONLv9dMjW9g8tBDD8mNN94oNWvWlLZt20rv3r3lsssuK/Q5tItKA5lhw4YFvI5mzZqZDNGhQ4ekTZs2+V5vbGys1KtXz/t9fHzgl9WXXnrJBFJaUJ1b8+bNTXBXpUoVKVeuXL73BSB06DID4CranfTxxx/L448/Lt9++63MnDlTXnzxRVO/48samXb55ZfLvffeK7fffru3APlM59ARXUOGDDHBzi233BLwOjR46t+/vwwYMEDmzZsne/bskc8//9xkohYvXhz07zVp0iRTAG7VCPnS+6lcubIZWaZF1XpfWjs0YsQI+e9//xv0fQEIHgERAFfRbMk777xjRnZpbZB2iWnhdO6Cal/jx483GR/tOivMOaZMmWJqdpYuXWoyTPnRwmYNiDTAatCggRmSv2HDhgJ/Jj8dOnQwt0CSk5Nl9erV5rya6dIM1+DBg00NERkjwBkxHh13CgAAEMXIEAEAgKhHQAQAAKIeAREAAIh6BEQAACDqERABAICoR0AEAACiHgERAACIegREAAAg6hEQAQCAqEdABAAAoh4BEQAAiHoERAAAQKLd/wfUBpQJRV0xyAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "import time\n", + "_, scores = genetic_algorithm(cost, qty, generations=50, pop_size=20)\n", "\n", - "results = []\n", - "for pop_size in [10, 20, 50]:\n", - " for mating_size in [2, 4, 6]:\n", - " for mut_prob in [0.1, 0.2, 0.4]:\n", - " start = time.time()\n", - " ga = pygad.GA(\n", - " num_generations=30,\n", - " num_parents_mating=mating_size,\n", - " fitness_func=fitness_func,\n", - " sol_per_pop=pop_size,\n", - " num_genes=1,\n", - " gene_space={'low': float(min_price), 'high': float(max_price)},\n", - " parent_selection_type=\"rank\",\n", - " keep_parents=2,\n", - " crossover_type=crossover_func,\n", - " mutation_type=mutation_func,\n", - " mutation_probability=mut_prob\n", - " )\n", - " ga.run()\n", - " best, fit, _ = ga.best_solution()\n", - " duration = time.time() - start\n", - " results.append({\n", - " \"pop_size\": pop_size,\n", - " \"mating_pool\": mating_size,\n", - " \"mutation_prob\": mut_prob,\n", - " \"best_fitness\": fit,\n", - " \"time_sec\": round(duration, 2)\n", - " })\n", - "\n", - "df_results = pd.DataFrame(results)\n", - "print(df_results)\n" + "import matplotlib.pyplot as plt\n", + "plt.plot(scores)\n", + "plt.xlabel(\"Поколение\")\n", + "plt.ylabel(\"Макс. прибыль\")\n", + "plt.title(\"Сходимость генетического алгоритма\")\n", + "plt.grid()\n", + "plt.show()\n" ] }, { @@ -615,14 +332,14 @@ "id": "2f79ae03", "metadata": {}, "source": [ - "- Самые высокие значения best_fitness достигаются при pop_size=50 и mutation_prob=0.1–0.2.\n", - "- Увеличение популяции с 10 → 50 приводит к улучшению результата, но также увеличивает время выполнения (0.02 → 0.08 сек).\n", - "- Слишком высокая мутация (0.4) даёт не лучшие результаты — например, строка 4: best_fitness=470.50.\n", + "## 10. Обоснование выбора функций\n", "\n", - "Общие рекомендации по результатам:\n", - "- pop_size=50\t- Даёт лучшие результаты, особенно если нужно точное решение\n", - "- mutation_prob=0.1–0.2\t- Оптимально для баланса между устойчивостью и разнообразием\n", - "- mating_pool=4–6\t- Лучше, чем 2 — больше комбинаций, выше качество" + "- Хромосома — цена: естественно моделируется как `float`.\n", + "- Кроссовер: обеспечивает плавное смешение стратегий (цен).\n", + "- Мутации: имитируют как локальный шум, так и случайный сбой.\n", + "- Фитнес — прибыль, что напрямую отражает цель бизнеса.\n", + "\n", + "Генетический алгоритм эффективен, так как перебор всех цен — ресурсоёмок, а градиент невозможен из-за дискретных qty.\n" ] } ], @@ -642,7 +359,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.6" + "version": "3.12.10" } }, "nbformat": 4,