September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -9,7 +9,7 @@ Starting with:
:* repeat until the parent converges, (hopefully), to the target.
;Related tasks:
;See also:
*   [[wp:Weasel_program#Weasel_algorithm|Weasel algorithm]].
*   [[wp:Evolutionary algorithm|Evolutionary algorithm]].

View file

@ -0,0 +1,88 @@
@echo off
setlocal enabledelayedexpansion
set target=M E T H I N K S @ I T @ I S @ L I K E @ A @ W E A S E L
set chars=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z @
set tempcount=0
for %%i in (%target%) do (
set /a tempcount+=1
set target!tempcount!=%%i
)
call:parent
echo %target%
echo --------------------------------------------------------
:loop
call:fitness parent
set currentfit=%errorlevel%
if %currentfit%==28 goto end
echo %parent% - %currentfit% [%attempts%]
set attempts=0
:innerloop
set /a attempts+=1
title Attemps - %attempts%
call:mutate %parent%
call:fitness tempparent
set newfit=%errorlevel%
if %newfit% gtr %currentfit% (
set tempcount=0
set "parent="
for %%i in (%tempparent%) do (
set /a tempcount+=1
set parent!tempcount!=%%i
set parent=!parent! %%i
)
goto loop
)
goto innerloop
:end
echo %parent% - %currentfit% [%attempts%]
echo Done.
exit /b
:parent
set "parent="
for /l %%i in (1,1,28) do (
set /a charchosen=!random! %% 27 + 1
set tempcount=0
for %%j in (%chars%) do (
set /a tempcount+=1
if !charchosen!==!tempcount! (
set parent%%i=%%j
set parent=!parent! %%j
)
)
)
exit /b
:fitness
set fitness=0
set array=%1
for /l %%i in (1,1,28) do if !%array%%%i!==!target%%i! set /a fitness+=1
exit /b %fitness%
:mutate
set tempcount=0
set returnarray=tempparent
set "%returnarray%="
for %%i in (%*) do (
set /a tempcount+=1
set %returnarray%!tempcount!=%%i
set %returnarray%=!%returnarray%! %%i
)
set /a tomutate=%random% %% 28 + 1
set /a mutateto=%random% %% 27 + 1
set tempcount=0
for %%i in (%chars%) do (
set /a tempcount+=1
if %mutateto%==!tempcount! (
set %returnarray%!tomutate!=%%i
)
)
set "%returnarray%="
for /l %%i in (1,1,28) do set %returnarray%=!%returnarray%! !%returnarray%%%i!
exit /b

View file

@ -97,8 +97,7 @@ int main()
{
std::cout << parent << ": " << fitness << "\n";
double const mutation_rate = 0.02 + (0.9*fitness)/initial_fitness;
typedef std::vector<std::string> childvec;
childvec childs;
std::vector<std::string> childs;
childs.reserve(C+1);
childs.push_back(parent);

View file

@ -1,33 +0,0 @@
import std.stdio, std.random, std.algorithm, std.range, std.ascii;
void evolve(string target, double prob=0.05, int C=100) {
auto chars = uppercase ~ " ";
char rndCh() { return chars[uniform(0, $)]; }
void mutate(char[] parent, char[] child) {
foreach (i, ref c; child)
c = uniform(0.0, 1.0) < prob ? rndCh() : parent[i];
}
int fitness(char[] subject, string target) {
return count!q{ a[0] != a[1] }(zip(subject, target));
}
auto parent= map!(i => rndCh())(target).array();
auto best = parent.dup;
auto child = new char[target.length];
int currDist = fitness(parent, target);
for (int gen; currDist > 0; gen++) {
foreach (_; 0 .. C) {
mutate(parent, child);
int dist = fitness(child, target);
if (dist < currDist) {
currDist = dist;
best[] = child[];
}
}
parent = best;
writefln("Generation %2s, dist=%2s: %s", gen, currDist, best);
}
}
void main() {
evolve("METHINKS IT IS LIKE A WEASEL");
}

View file

@ -1,19 +0,0 @@
import std.stdio, std.random, std.algorithm, std.range;
enum target = "METHINKS IT IS LIKE A WEASEL"d;
enum C = 100; // Number of children in each generation.
enum P = 0.05; // Mutation probability.
auto alphabet = " ABCDEFGHIJLKLMNOPQRSTUVWXYZ"d.dup;
const fitness = (dchar[] s) => count!"a[0] != a[1]"(zip(s, target));
const rndc = () => alphabet[uniform(0, $)];
const mutate = (dchar[] s) =>
s.map!(a => uniform(0., 1.) < P ? rndc() : a)().array();
void main() {
auto parent = target.length.iota().map!(_ => rndc())().array();
for (int gen = 1; parent != target; gen++) {
auto offs = parent.repeat(C).map!mutate().array();
parent = offs.minPos!((a, b) => fitness(a) < fitness(b))()[0];
writefln("Gen %2d, dist=%2d: %s", gen, fitness(parent), parent);
}
}

View file

@ -1,65 +1,68 @@
#import system.
#import system'routines.
#import extensions.
import system'routines.
import extensions.
#symbol Target = "METHINKS IT IS LIKE A WEASEL".
#symbol AllowedCharacters = " ABCDEFGHIJKLMNOPQRSTUVWXYZ".
#symbol C = 100.
#symbol P = 0.05r.
#symbol rnd = randomGenerator.
const Target = "METHINKS IT IS LIKE A WEASEL".
const AllowedCharacters = " ABCDEFGHIJKLMNOPQRSTUVWXYZ".
#symbol randomChar
= AllowedCharacters @ (rnd nextInt:(AllowedCharacters length)).
const C = 100.
const P = 0.05r.
#class(extension) evoHelper
rnd = randomGenerator.
randomChar
= AllowedCharacters[rnd nextInt(AllowedCharacters length)].
extension evoHelper
{
#method randomString
= 0 repeat &till:self &each:x [ randomChar ] summarize:(String new) literal.
randomString
= 0 till:self repeat(:x)( randomChar ); summarize:(String new); literal.
#method fitness &of:s
= self zip:s &into:(:a:b)[ (a == b)iif:1:0 ] summarize:(Integer new) int.
fitnessOf:s
= self zip:s by(:a:b)( (a == b)iif(1,0) ); summarize(Integer new); int.
#method mutate : p
= self select &each: ch [ (rnd nextReal <= p) iif:randomChar:ch ] summarize:(String new) literal.
mutate : p
= self selectBy(:ch)( (rnd nextReal <= p) iif(randomChar,ch) ); summarize(String new); literal.
}
#class EvoAlgorithm :: Enumerator
class EvoAlgorithm :: Enumerator
{
#field theTarget.
#field theCurrent.
#field theVariantCount.
object theTarget.
object theCurrent.
object theVariantCount.
#constructor new : s &of:count
constructor new : s of:count
[
theTarget := s.
theVariantCount := count int.
]
#method get = theCurrent.
get = theCurrent.
#method next
next
[
($nil == theCurrent)
? [ theCurrent := theTarget length randomString. ^ true. ].
if ($nil == theCurrent)
[ theCurrent := theTarget length; randomString. ^ true ].
(theTarget == theCurrent)
? [ ^ false. ].
if (theTarget == theCurrent)
[ ^ false ].
#var variants := Array new:theVariantCount set &every:(&index:x) [ theCurrent mutate:P ].
var variants := Array new:theVariantCount; populate(:x)( theCurrent mutate:P ).
theCurrent := variants array sort:(:a:b) [ a fitness &of:Target > b fitness &of:Target ] getAt:0.
theCurrent := variants array; sort(:a:b)( a fitnessOf:Target > b fitnessOf:Target ); getAt:0.
^ true.
]
}
#symbol program =
program =
[
#var attempt := Integer new.
EvoAlgorithm new:Target &of:C run &each:current
var attempt := Integer new.
EvoAlgorithm new:Target of:C; forEach(:current)
[
console
writeLiteral:"#":(attempt += 1) &paddingLeft:10
writeLine:" ":current:" fitness: ":(current fitness &of:Target).
printPaddingLeft(10,"#",attempt append:1);
printLine(" ",current," fitness: ",current fitnessOf:Target).
].
console readChar.
].

View file

@ -1,15 +0,0 @@
defmodule Rand do
def init do # Initialize the random values.
<< a :: 32, b :: 32, c :: 32 >> = :crypto.strong_rand_bytes(12)
:random.seed(a,b,c)
end
def num do # Returns a value between 0.0 and 1.0.
init
:random.uniform
end
def char(list) do # Returns a random letter or a space.
Enum.at(list, :random.uniform(length(list)) - 1)
end
end

View file

@ -1,9 +0,0 @@
defmodule Log do
def show(offspring,i) do
IO.puts "Generation: #{i}, Offspring: #{offspring}"
end
def found([target|i]) do
IO.puts "#{target} found in #{i} iterations"
end
end

View file

@ -1,57 +0,0 @@
defmodule Evolution do
def select(target) do
# Generate char list from A to Z; 32 is the ord value for space.
chars = (?A..?Z) |> Enum.to_list() |> List.insert_at(0, 32)
(1..String.length(target)) # Creates parent for generation 0.
|> Enum.map(fn _-> Rand.char(chars) end)
|> mutate(String.to_char_list(target),0,[],chars)
|> Log.found()
end
# w is used to denote fitness in population genetics.
def mutate(parent,target,i,_,_) when target == parent, do: [parent|i]
def mutate(parent,target,i,_,chars) when target != parent do
w = fitness(parent,target)
prev = reproduce(target,parent,w,0,mu_rate(w),chars)
# Check if the most fit member of the new gen has a greater fitness than the parent.
if w < fitness(prev,target) do
parent = prev
Log.show(parent,i)
end
mutate(parent,target,i+1,prev,chars)
end
# Generate 100 offspring and select the one with the greatest fitness.
def reproduce(target,parent,_,_,rate,chars) do
(1..100)
|> Enum.to_list()
|> Stream.map(fn _-> mutation(parent,rate,chars) end)
|> List.insert_at(0, parent)
|> Enum.max_by(fn n -> fitness(n,target) end)
end
# Calculate fitness by checking difference between parent and offspring chars.
def fitness(t,r) do
(0..length(t)-1)
|> Stream.map(fn n -> abs(Enum.at(t,n) - Enum.at(r,n)) end)
|> Enum.reduce(fn a,b -> a + b end)
|> calc()
end
# Generate offspring based on parent.
def mutation(p,r,chars) do
# Copy the parent chars, then check each val against the random mutation rate
(0..length(p)-1)
|> Stream.map(fn n -> Enum.at(p,n) end)
|> Enum.map(fn n -> if Rand.num <= r, do: Rand.char(chars), else: n end)
end
def calc(sum), do: 100 * :math.exp(sum/-10)
def mu_rate(n), do: 1 - :math.exp(-(100-n)/400)
end

View file

@ -1 +0,0 @@
Evolution.select("METHINKS IT IS LIKE A WEASEL")

View file

@ -15,7 +15,7 @@ defmodule Evolution do
def select(target) do
(1..String.length(target)) # Creates parent for generation 0.
|> Enum.map(fn _-> Enum.random(@chars) end)
|> mutate(to_char_list(target),0)
|> mutate(to_charlist(target),0)
|> Log.found
end
@ -28,16 +28,17 @@ defmodule Evolution do
# Check if the most fit member of the new gen has a greater fitness than the parent.
if w < fitness(prev,target) do
parent = prev
Log.show(parent,i)
Log.show(prev,i)
mutate(prev,target,i+1)
else
mutate(parent,target,i+1)
end
mutate(parent,target,i+1)
end
# Generate 100 offspring and select the one with the greatest fitness.
defp reproduce(target,parent,rate) do
[parent | Enum.map(1..100, fn _-> mutation(parent,rate) end)]
[parent | (for _ <- 1..100, do: mutation(parent,rate))]
|> Enum.max_by(fn n -> fitness(n,target) end)
end

View file

@ -1,27 +1,22 @@
function evolve(parent,target,mutation_rate,num_children)
println("Initial parent is $parent, its fitness is $(fitness(parent,target))")
gens=0
while parent!=target
children=[mutate(parent,mutation_rate) for i=1:num_children]
bestfit,best=findmax(map(child->fitness(child,target),children))
parent=children[best]
gens+=1
if gens%10==0
println("After $gens generations, the new parent is $parent and its fitness is $(fitness(parent,target))")
end
end
println("After $gens generations, the parent evolved into the target $target")
function evolve(parent::String, target::String, mutrate::Float64, nchild::Int)
fitness(a, b::String=target) = count(l == t for (l, t) in zip(a, b))
function mutate(str::String, rate::Float64=mutrate)
L = split(" ABCDEFGHIJKLMNOPQRSTUVWXYZ", "")
r = collect(rand() < rate ? rand(L) : c for c in str)
return string(r...)
end
println("Initial parent is $parent, its fitness is $(fitness(parent))")
gens = 0
while parent != target
children = collect(mutate(parent, mutrate) for i in 1:nchild)
bestfit, best = findmax(fitness.(children))
parent = children[best]
gens += 1
if gens % 10 == 0
println("After $gens generations, the new parent is $parent and its fitness is $(fitness(parent))")
end
end
println("After $gens generations, the parent evolved into the target $target")
end
fitness(s1,s2)=count(x->x,convert(Array{Char,1},s1).==convert(Array{Char,1},s2))
function mutate(s,rate)
new_s=""
for c in s
new_s*=string(rand()<rate?
" ABCDEFGHIJKLMNOPQRSTUVWXYZ"[rand(1:27)]:c)
end
return new_s
end
evolve("IU RFSGJABGOLYWF XSMFXNIABKT","METHINKS IT IS LIKE A WEASEL",0.08998,100)
evolve("IU RFSGJABGOLYWF XSMFXNIABKT", "METHINKS IT IS LIKE A WEASEL", 0.08998, 100)

View file

@ -0,0 +1,34 @@
import java.util.*
val target = "METHINKS IT IS LIKE A WEASEL"
val validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
val random = Random()
fun randomChar() = validChars[random.nextInt(validChars.length)]
fun hammingDistance(s1: String, s2: String) =
s1.zip(s2).map { if (it.first == it.second) 0 else 1 }.sum()
fun fitness(s1: String) = target.length - hammingDistance(s1, target)
fun mutate(s1: String, mutationRate: Double) =
s1.map { if (random.nextDouble() > mutationRate) it else randomChar() }
.joinToString(separator = "")
fun main(args: Array<String>) {
val initialString = (0 until target.length).map { randomChar() }.joinToString(separator = "")
println(initialString)
println(mutate(initialString, 0.2))
val mutationRate = 0.05
val childrenPerGen = 50
var i = 0
var currVal = initialString
while (currVal != target) {
i += 1
currVal = (0..childrenPerGen).map { mutate(currVal, mutationRate) }.maxBy { fitness(it) }!!
}
println("Evolution found target after $i generations")
}

View file

@ -3,7 +3,7 @@ constant mutate_chance = .08;
constant @alphabet = flat 'A'..'Z',' ';
constant C = 100;
sub mutate { $^string.subst(/./, { rand < mutate_chance ?? @alphabet.pick !! $/ }, :g) }
sub mutate { [~] (rand < mutate_chance ?? @alphabet.pick !! $_ for $^string.comb) }
sub fitness { [+] $^string.comb Zeq state @ = target.comb }
loop (

View file

@ -45,7 +45,7 @@ actor Main
var i: USize = 0
while i < trial.size() do
try
if trial(i) == _target(i) then
if trial(i)? == _target(i)? then
ret_val = ret_val + 1
end
end
@ -64,7 +64,7 @@ actor Main
if rnd_real <= rate then
let rnd_int: U64 = _rand.int(_possibilities.size().u64())
try
ret_val.push(_possibilities(rnd_int.usize()))
ret_val.push(_possibilities(rnd_int.usize())?)
end
else
ret_val.push(char)

View file

@ -26,7 +26,7 @@ fitness: parse arg x; $=0; do k=1 for Ltar; $=$+(substr(x,k,1)==substr(target,
return $
/*──────────────────────────────────────────────────────────────────────────────────────*/
mutate: parse arg x,rate; $= /*set X to 1st argument, RATE to 2nd.*/
do j=1 for Ltar; r=random(1,100000) /*REXX's max for RANSOM*/
do j=1 for Ltar; r=random(1,100000) /*REXX's max for RANDOM*/
if .00001*r<=rate then $=$ || substr(abc,r//Labc+1, 1)
else $=$ || substr(x ,j , 1)
end /*j*/

View file

@ -1,45 +0,0 @@
/*REXX program demonstrates an evolutionary algorithm (using mutation).*/
parse arg children MR seed . /*get options (maybe) from C.L. */
if children=='' then children = 10 /*# of children produced each gen*/
if MR =='' then MR = "4%" /*the char Mutation Rate each gen*/
if right(MR,1)=='%' then MR=strip(MR,,"%")/100 /*expressed as %? Adjust*/
if seed\=='' then call random ,,seed /*allow the runs to be repeatable*/
abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' ; Labc=length(abc)
do i=0 for Labc /*define array (faster compare), */
A.i=substr(abc, i+1, 1) /* it's better than picking out a*/
end /*i*/ /* byte from a character string. */
target= 'METHINKS IT IS LIKE A WEASEL' ; Ltar=length(target)
do i=1 for Ltar /*define array (faster compare), */
T.i=substr(target, i, 1) /*it's better than a byte-by-byte*/
end /*i*/ /*compare using character strings*/
parent= mutate( left('', Ltar), 1) /*gen rand str,same length as tar*/
say center('target string', Ltar, '') "children" 'mutationRate'
say target center(children, 8) center((MR*100/1)'%', 12) ; say
say center('new string', Ltar, '') "closeness" 'generation'
do gen=0 until parent==target; close=fitness(parent)
almost=parent
do children; child=mutate(parent, MR)
_=fitness(child); if _<=close then iterate
close=_; almost=child
say almost right(close, 9) right(gen, 10)
end /*children*/
parent=almost
end /*gen*/
exit /*stick a fork in it, we're done.*/
/*───────────────────────────────────FITNESS subroutine─────────────────*/
fitness: parse arg x; hit=0; do k=1 for Ltar
hit=hit + (substr(x,k,1) == T.k)
end /*k*/
return hit
/*───────────────────────────────────MUTATE subroutine──────────────────*/
mutate: parse arg x,rate,? /*set ? to a null, x=string. */
do j=1 for Ltar; r=random(1, 100000)
if .00001*r<=rate then do; _=r//Labc; ?=? || A._; end
else ?=? || substr(x,j,1)
end /*j*/
return ?

View file

@ -0,0 +1,57 @@
(import (scheme base)
(scheme write)
(srfi 27)) ; random numbers
(random-source-randomize! default-random-source)
(define target "METHINKS IT IS LIKE A WEASEL") ; target string
(define C 100) ; size of population
(define p 0.1) ; chance any char is mutated
;; return a random character in given range
(define (random-char)
(string-ref "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
(random-integer 27)))
;; compute distance of given string from target
(define (fitness str)
(apply +
(map (lambda (c1 c2) (if (char=? c1 c2) 0 1))
(string->list str)
(string->list target))))
;; mutate given parent string, returning a new string
(define (mutate str)
(string-map (lambda (c)
(if (< (random-real) p)
(random-char)
c))
str))
;; create a population by mutating parent,
;; returning a list of variations
(define (make-population parent)
(do ((pop '() (cons (mutate parent) pop)))
((= C (length pop)) pop)))
;; find the most fit candidate in given list
(define (find-best candidates)
(define (select-best a b)
(if (< (fitness a) (fitness b)) a b))
;
(do ((best (car candidates) (select-best best (car rem)))
(rem (cdr candidates) (cdr rem)))
((null? rem) best)))
;; create first parent from random characters
;; of same size as target string
(define (initial-parent)
(do ((res '() (cons (random-char) res)))
((= (length res) (string-length target))
(list->string res))))
;; run the search
(do ((parent (initial-parent) (find-best (cons parent (make-population parent))))) ; select best from parent and population
((string=? parent target)
(display (string-append "Found: " parent "\n")))
(display parent) (newline))

View file

@ -0,0 +1,43 @@
10 LET A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ "
20 LET T$="METHINKS IT IS LIKE A WEASEL"
30 LET L=LEN T$
40 LET C=10
50 LET M=0.05
60 LET G=0
70 DIM C$(C,L)
80 LET P$=""
90 FOR I=1 TO L
100 LET P$=P$+A$(INT (RND*LEN A$)+1)
110 NEXT I
120 PRINT AT 1,0;P$
130 LET S$=P$
140 GOSUB 390
150 LET N=R
160 PRINT AT 1,30;N
170 PRINT AT 0,4;G
180 IF P$=T$ THEN GOTO 440
190 FOR I=1 TO C
200 FOR J=1 TO L
210 LET C$(I,J)=P$(J)
220 IF RND<=M THEN LET C$(I,J)=A$(INT (RND*LEN A$)+1)
230 PRINT AT I+2,J-1;C$(I,J)
240 NEXT J
250 PRINT AT I+2,30;" "
260 NEXT I
270 LET F=0
280 FOR I=1 TO C
290 LET S$=C$(I)
300 GOSUB 390
310 PRINT AT I+2,30;R
320 IF R>N THEN LET F=I
330 IF R>N THEN LET N=R
340 NEXT I
350 IF F>0 THEN LET P$=C$(F)
360 LET G=G+1
370 PRINT AT 1,0;P$
380 GOTO 160
390 LET R=0
400 FOR K=1 TO L
410 IF S$(K)=T$(K) THEN LET R=R+1
420 NEXT K
430 RETURN

View file

@ -0,0 +1,98 @@
'This is the string we want to "evolve" to. Any string of any length will
'do as long as it consists only of upper case letters and spaces.
Target = "METHINKS IT IS LIKE A WEASEL"
'This is the pool of letters that will be selected at random for a mutation
letters = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
'A mutation rate of 0.5 means that there is a 50% chance that one letter
'will be mutated at random in the next child
mutation_rate = 0.5
'Set for 10 children per generation
Dim child(10)
'Generate the first guess as random letters
Randomize
Parent = ""
for i = 1 to len(Target)
Parent = Parent & Mid(letters,Random(1,Len(letters)),1)
next
gen = 0
Do
bestfit = 0
bestind = 0
gen = gen + 1
'make n copies of the current string and find the one
'that best matches the target string
For i = 0 to ubound(child)
child(i) = Mutate(Parent, mutation_rate)
fit = Fitness(Target, child(i))
If fit > bestfit Then
bestfit = fit
bestind = i
End If
Next
'Select the child that has the best fit with the target string
Parent = child(bestind)
Wscript.Echo parent, "(fit=" & bestfit & ")"
Loop Until Parent = Target
Wscript.Echo vbcrlf & "Generations = " & gen
'apply a random mutation to a random character in a string
Function Mutate ( ByVal str , ByVal rate )
Dim pos 'a random position in the string'
Dim ltr 'a new letter chosen at random '
If rate > Rnd(1) Then
ltr = Mid(letters,Random(1,len(letters)),1)
pos = Random(1,len(str))
str = Left(str, pos - 1) & ltr & Mid(str, pos + 1)
End If
Mutate = str
End Function
'returns the number of letters in the two strings that match
Function Fitness (ByVal str , ByVal ref )
Dim i
Fitness = 0
For i = 1 To Len(str)
If Mid(str, i, 1) = Mid(ref, i, 1) Then Fitness = Fitness + 1
Next
End Function
'Return a random integer in the range lower to upper (inclusive)
Private Function Random ( lower , upper )
Random = Int((upper - lower + 1) * Rnd + lower)
End Function

View file

@ -0,0 +1,14 @@
const target = "METHINKS IT IS LIKE A WEASEL";
const C = 100; // Number of children in each generation.
const P = 0.05; // Mutation probability.
const A2ZS = ["A".."Z"].walk().append(" ").concat();
fcn fitness(s){ Utils.zipWith('!=,target,s).sum(0) } // bigger is worser
fcn rnd{ A2ZS[(0).random(27)] }
fcn mutate(s){ s.apply(fcn(c){ if((0.0).random(1) < P) rnd() else c }) }
parent := target.len().pump(String,rnd); // random string of "A..Z "
gen:=0; do{ // mutate C copies of parent and pick the fittest
parent = (0).pump(C,List,T(Void,parent),mutate)
.reduce(fcn(a,b){ if(fitness(a)<fitness(b)) a else b });
println("Gen %2d, dist=%2d: %s".fmt(gen+=1, fitness(parent), parent));
}while(parent != target);