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

707 lines
31 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Genetiske Algoritmer\n",
"\n",
"Denne notesbog er en del af [AI for Beginners Curriculum](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": [
"## Noget Teori\n",
"\n",
"**Genetiske Algoritmer** (GA) er baseret på en **evolutionær tilgang** til AI, hvor metoder til evolution af en population bruges til at finde en optimal løsning på et givet problem. De blev foreslået i 1975 af [John Henry Holland](https://en.wikipedia.org/wiki/John_Henry_Holland).\n",
"\n",
"Genetiske Algoritmer bygger på følgende ideer:\n",
"* Gyldige løsninger på problemet kan repræsenteres som **gener**\n",
"* **Crossover** giver os mulighed for at kombinere to løsninger for at opnå en ny gyldig løsning\n",
"* **Selektionsprocessen** bruges til at vælge mere optimale løsninger ved hjælp af en **fitnessfunktion**\n",
"* **Mutationer** introduceres for at destabilisere optimeringen og hjælpe os med at undgå lokale minima\n",
"\n",
"Hvis du vil implementere en Genetisk Algoritme, skal du bruge følgende:\n",
"\n",
" * En metode til at kode vores problemløsninger ved hjælp af **gener** $g\\in\\Gamma$\n",
" * På sættet af gener $\\Gamma$ skal vi definere en **fitnessfunktion** $\\mathrm{fit}: \\Gamma\\to\\mathbb{R}$. Mindre funktionsværdier svarer til bedre løsninger.\n",
" * En mekanisme til **crossover**, der kombinerer to gener for at få en ny gyldig løsning $\\mathrm{crossover}: \\Gamma^2\\to\\Gamma$.\n",
" * En mekanisme til **mutation** $\\mathrm{mutate}: \\Gamma\\to\\Gamma$.\n",
"I mange tilfælde er crossover og mutation ret simple algoritmer, der manipulerer gener som numeriske sekvenser eller bitvektorer.\n",
"\n",
"Den specifikke implementering af en genetisk algoritme kan variere fra tilfælde til tilfælde, men den overordnede struktur er som følger:\n",
"\n",
"1. Vælg en startpopulation $G\\subset\\Gamma$\n",
"2. Vælg tilfældigt en af de operationer, der skal udføres i dette trin: crossover eller mutation \n",
"3. **Crossover**:\n",
" * Vælg tilfældigt to gener $g_1, g_2 \\in G$\n",
" * Beregn crossover $g=\\mathrm{crossover}(g_1,g_2)$\n",
" * Hvis $\\mathrm{fit}(g)<\\mathrm{fit}(g_1)$ eller $\\mathrm{fit}(g)<\\mathrm{fit}(g_2)$ - erstat det tilsvarende gen i populationen med $g$.\n",
"4. **Mutation** - vælg et tilfældigt gen $g\\in G$ og erstat det med $\\mathrm{mutate}(g)$\n",
"5. Gentag fra trin 2, indtil vi opnår en tilstrækkelig lille værdi af $\\mathrm{fit}$, eller indtil grænsen for antallet af trin er nået.\n",
"\n",
"Opgaver, der typisk løses med GA:\n",
"1. Optimering af tidsplaner\n",
"1. Optimal pakning\n",
"1. Optimal skæring\n",
"1. Acceleration af udtømmende søgning\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem 1: Retfærdig Opdeling af Skatte\n",
"\n",
"**Opgave**: \n",
"To personer har fundet en skat, der indeholder diamanter i forskellige størrelser (og tilsvarende forskellige priser). De skal dele skatten i to dele på en sådan måde, at prisforskellen er 0 (eller minimal).\n",
"\n",
"**Formel definition**: \n",
"Vi har et sæt af tal $S$. Vi skal dele det op i to delmængder $S_1$ og $S_2$, sådan at $$\\left|\\sum_{i\\in S_1}i - \\sum_{j\\in S_2}j\\right|\\to\\min$$ og $S_1\\cup S_2=S$, $S_1\\cap S_2=\\emptyset$.\n",
"\n",
"Først og fremmest, lad os definere sættet $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": [
"Lad os kode hver mulig løsning af problemet med en binær vektor $B\\in\\{0,1\\}^N$, hvor tallet på den $i$-te position viser, hvilken af mængderne ($S_1$ eller $S_2$) det $i$-te tal i den oprindelige mængde $S$ tilhører. `generate`-funktionen vil generere disse tilfældige binære vektorer.\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": [
"Lad os nu definere `fit`-funktionen, der beregner \"omkostningen\" ved løsningen. Det vil være forskellen mellem summen af to sæt, $S_1$ og $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": [
"Nu skal vi definere funktioner til mutation og crossover:\n",
"* For mutation vil vi vælge én tilfældig bit og negere den (ændre fra 0 til 1 og omvendt)\n",
"* For crossover vil vi tage nogle bits fra én vektor og nogle bits fra en anden. Vi vil bruge den samme `generate`-funktion til tilfældigt at vælge, hvilke bits der skal tages fra hvilken af inputmaskerne.\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": [
"Lad os oprette den oprindelige population af løsningerne $P$ med størrelsen `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": [
"Nu, hovedfunktionen til at udføre evolutionen. `n` er antallet af evolutionstrin, der skal gennemgås. Ved hvert trin:\n",
"* Med en sandsynlighed på 30 % udfører vi en mutation og erstatter elementet med den dårligste `fit`-funktion med det muterede element\n",
"* Med en sandsynlighed på 70 % udfører vi crossover\n",
"\n",
"Funktionen returnerer den bedste løsning (gen svarende til den bedste løsning) og historikken over den minimale fit-funktion i populationen ved hver iteration.\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": [
"Du kan se, at vi har formået at minimere `fit`-funktionen en del! Her er grafen, der viser, hvordan `fit`-funktionen for hele populationen opfører sig under processen.\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": [
"## Problem 2: N Queens Problem\n",
"\n",
"**Opgave**:\n",
"Du skal placere $N$ dronninger på et skakbræt af størrelsen $N\\times N$ på en måde, så de ikke angriber hinanden.\n",
"\n",
"Først og fremmest, lad os løse problemet uden genetiske algoritmer, ved at bruge fuld søgning. Vi kan repræsentere brættets tilstand med listen $L$, hvor det $i$-te tal i listen er den horisontale position af dronningen i den $i$-te række. Det er ret indlysende, at hver løsning kun vil have én dronning per række, og hver række vil have en dronning.\n",
"\n",
"Vores mål vil være at finde den første løsning til problemet, hvorefter vi stopper søgningen. Du kan nemt udvide denne funktion til at generere alle mulige positioner for dronninger.\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": [
"Lad os nu måle, hvor lang tid det tager at finde en løsning på 20-dronningeproblemet:\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": [
"Lad os løse det samme problem ved hjælp af genetisk algoritme. Denne løsning er inspireret af [denne blogpost](https://kushalvyas.github.io/gen_8Q.html).\n",
"\n",
"Vi vil repræsentere hver løsning med den samme liste af længde $N$, og som en `fit`-funktion vil vi tage antallet af dronninger, der angriber hinanden:\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": [
"Da beregning af fitnessfunktionen er tidskrævende, lad os gemme hver løsning i populationen sammen med værdien af fitnessfunktionen. Lad os generere den indledende population:\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": [
"Nu skal vi definere mutations- og crossover-funktioner. Crossover vil kombinere to gener ved at bryde dem på et tilfældigt punkt og sammenkæde to dele fra forskellige gener.\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": [
"Nu skal vi definere den primære evolutionære løkke. Vi vil gøre logikken lidt anderledes end det tidligere eksempel for at vise, at man kan være kreativ. Vi vil gentage løkken, indtil vi opnår den perfekte løsning (fitness-funktion=0), og ved hvert trin tager vi den nuværende generation og producerer en ny generation af samme størrelse. Dette gøres ved hjælp af funktionen `nxgeneration` og følgende trin:\n",
"\n",
"1. Kassér de mindst egnede løsninger - der findes en funktion `discard_unfit`, som gør dette\n",
"1. Tilføj nogle flere tilfældige løsninger til generationen\n",
"1. Skab en ny generation af størrelse `gen_size` ved at følge disse trin for hver ny gen:\n",
" - vælg to tilfældige gener med sandsynlighed proportional med fitness-funktionen\n",
" - beregn en crossover\n",
" - anvend en mutation med sandsynligheden `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": [
"Det er interessant, at vi for det meste er i stand til at finde en løsning ret hurtigt, men i nogle sjældne tilfælde når optimeringen et lokalt minimum, og processen sidder fast i lang tid. Det er vigtigt at tage dette i betragtning, når du måler gennemsnitlig tid: mens genetiske algoritmer i de fleste tilfælde vil være hurtigere end fuld søgning, kan det i nogle tilfælde tage længere tid. For at løse dette problem giver det ofte mening at begrænse antallet af generationer, der skal overvejes, og hvis vi ikke er i stand til at finde løsningen - kan vi starte forfra.\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**Ansvarsfraskrivelse**: \nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.\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-28T16:10:18+00:00",
"source_file": "lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb",
"language_code": "da"
}
},
"nbformat": 4,
"nbformat_minor": 2
}