| ||||||||||||||
| ||||||||||||||
|
||||||||||||||
Genetic Algorithm with Floating Point in AssemblerBy Manabu Ishii
PurposeThe purpose of this article is introduce how to make Genetic Algorithm @asm, and how to make assembly program, and my English skill up. I don't have good English and enough knowledge about GA and FP. By writing this article, I'll teach about GA, FP, and English to me. If you read this, soon you notice that I like to use "don't know". When I use the words, I really don't know (hmm this is first time, you see). If you know about it please teach it to me, and I want to fix it.When I started writing this article, I was going to make the series of lesson. But during writing, this article became one article by itself. So something strange is appeared in place. If you have enough time and enough perseverance, I want you to read through the whole article at first. And read again, then you can understand...I hope so. CompilerI use NASM.FirstFirst program we make is only quit. Nothing is done. But it's important how to know quit. Following code is ga001.asm:--- ga001.asm --- org 0100h start: ; quit program. ret --- ga001.asm ---Line 1 is declaration that this program start at 0100h. Line 2 is the label, if you want to back to start, you can use this label. Line 3 is comment. At nasm ';' is comment start and anything after this is ignored. Line 4 is command which is only this program have. And this statement( command ) is quit the program (exactly it has described another expression... but now I use it for QUIT the program).
CompileNext, how to compile it ?--- cut --- nasm -f bin -o ga001.com ga001.asm --- cut --- RunHow to use it? Let's invoke:--- magic word --- ga001 --- magic word ---Screen shot isn't available...but something like this. --- screen mimic --- C:\gaasm>nasm -f bin -o ga001.com ga001.asm C:\gaasm>ga001 C:\gaasm> --- mimic is dead ---Umm, it works well, but there's nothing...like Japanese Bank's rates of interest.
Before making GENE poolWe must construct the GENE pool and filled it random numbers. Method of random number generating is alot of type. It's heavy factor of GA, though I currently choose easy one which is work. Of course you can prepare own fantastic random routine. To randomize I use following interupt (DOS only):--- from Ralf's interupt list --- AH = 00h Return: CX:DX = number of clock ticks since midnight AL = midnight flag, nonzero if midnight passed since time last read --- from Ralf's interupt list ---How to invoke: --- randomize --- ;randomize initialize rand seed xor ah,ah ; ah = 0 int 1Ah ; get date and time --- randomize ---Umm, Why 'xor ah,ah' makes ah = 0 ? 'XOR' is exclusive OR . ---XOR---- A | B | R 0 | 0 | 0 0 | 1 | 1 1 | 0 | 1 1 | 1 | 0 ---XOR----As you see, if A and B is same (A is set and B is set or, both is unset). Result is 0 and if A and B isn't same, Result = 1. If we take ah as A, and ah as B, all bits are the same, so ah = 0. Feed seed: --- feed seed --- ; dx is wide range ; cx is small...? ; so we take cx as 1st seed and dx as 2nd seed mov [seed1],cx ; save seed1 mov [seed2],dx ; save seed2 --- feed seed ---Now Random generating routine have gotten two seeds. I made following routine, I don't know it work pretty well. But as far as I dumped, It seem to work enough well and pretty complex to explain it...So I don't explain it :), plz don't complain for my lazy. --- random generate --- ; generate random number ; return ; ax = number random: db 0b8h ; mov ax,seed1 seed1: db 0,0 dec ax ; decrement randloop: db 35h ; xor ax,seed2 seed2: db 0,0 inc ax ; xchg ah,al xor [seed2],ax ; 0.092 sub ax,[seed1] rol al,1 ; 0.091 xchg ah,al ; 0.092 sub [seed1],ax ; change seed1 ret --- random generate ---ver 0.091, I fix 'rol al,1' to 'ror al,1', it works better? ver 0.092, 'xchg ah,al' before 'sub [seed1],ax'. 'xor [seed2],ax' before 'sub ax,[seed1]. Hi ppl, now we can generate any random number (I hope so ...) Finally, we stand the START line. Let's make GENE POOL !! But before make GENE POOL, we have something to decide. Because what kind of gene expression we use is important. And it's depend on PROBLEM which we solve. Umm, making problem is harder than solve it :)...joke. I make 1 problem. It's a find the minimum value of fuction which has some Sin/Cos functions. I want to choose easy one. Everyone can understand where is the minimum, and if the function have LOCAL minimum is better. Exactly I don't have any idea of function's shape. So it's come back later. But I decide the range of search space between to 0 to 1. I make start line, only we need is the whistle ( NOT **'s whistler :-) To make GENE POOLFirst we decide the number of population. ( hmm, there is another problem ) I don't know how range I can use ... So I gather 100 population for our system.--- something from 004 --- POPULATION equ 100 ; population of pool --- end of c&p ---This line is the population. If you want change population, you can take any number. ( I don't know where is limit ...) We decide the number of population. We make the pool with GENEs. Following code is pool maker . --- this is pool maker --- mov cx,POPULATION * 4 ; why multiply by 4 ? A gene have 4 byte (32 bit)! mov di,randnum loop1: call random ; generate random number stosb ; we store the number which we generate to [di] loop loop1 --- this is pool maker ---cx is which time we loop(s). stosb is value of AL into address which is indicated by di. Now we have made the pool full of GENEs. EvaluateHow do we evaluate it ? hmm I copied this from test functions--- here's function ---
Rosenbrock's Saddle
F(x) = Sum(i=1,n) { 100*(x_(i+1)-x_i^2)^2 + (1-x_i)^2 }
-2.048 .le. x_i .le. 2.048
Minimum = 0.0 at x_i=1.0
This function has a long curved valley which is only slightly decreasing.
--- here's function ---
I decide when i = n, (i+1) = ( i + 1 ) mod i ,(ie. result 1 ).
And n = 2.So if I write 'y', 'y' means 'x+1' .
So fomula is following
--- extract function ---
F(x) = Sum(i=1,n) { 100*(x_(i+1)-x_i^2)^2 + (1-x_i)^2 }
= { 100 * ( y - x ^ 2 ) ^ 2 + ( 1 - x ) ^ 2 }
+ { 100 * ( x - y ^ 2 ) ^ 2 + ( 1 - y ) ^ 2 }
--- extract function ---
First we have x and y between -2.408 to 2.048
How do we implement this limitation ?
Remeber the remainder. If we divide the number by 4 and take the remainder of this, we get the number
which range is between 0 to 3.
So we have divide the number, which gene express, by 4096( 2048*2 ).
following code is :
--- set up x --- ; setup x between -2048 to 2048 fild dword [ range ] ; store denominator (4096, we need between 0 to 4096 ) fild dword [ di ] ; get gene for x fabs ; absolute 'ST0' fprem ; 0 < 'ST0' < 4096 fild dword [ rangef ] ; st0 = 2048, st1 = 0 < x < 4096 fsubp st1,st0 ; -2048 < 'ST0' < 2048 ; setup x between -2.048 to 2.048 fidiv word [ para2 ] --- set up x ---do the same, make y . --- set up y ( x + 1 ) --- ; setup y between -2048 to 2048 fild dword [ range ] ; store denominator (4096, we need between 0 to 4096 ) fild dword [ di + 4 ] ; get gene for y fabs ; absolute 'ST0' fprem ; 0 < 'ST0' < 4096 fild dword [ rangef ] ; st0 = 2048, st1 = 0 < y < 4096 fsubp st1,st0 ; -2048 < 'ST0' < 2048 ; setup y between -2.048 to 2.048 fidiv word [ para2 ] --- set up y ---Currently we have 4096 in two stacks. and we don't need them. so pop them. Oops I did it again ... We load integer value and divide by integer ,so we get remainder as integer value. So we only have 4096 diffent value... It's too narrow. I solve this problem with division 1000000. Following code is revised: --- division --- fabs ; absolute 'ST0' fdiv dword [ million ] ; ver 0.094 fprem ; 0 < 'ST0' < 4096 --- division ---Of course, we do this both x and y . I notice that problem is between 'dd 1000000.0' and 'dd 1000000'. for example: --- datas --- million: dd 1000000.0 million2: dd 1000000 --- datas --- --- code --- fld dword [ million ] fld dword [ million2 ] --- code --- --- result --- st0=+1.401298E-0039 st1=+1.000000E+0006 --- result ---hmm, I think these aren't same value... If you make something floating point value, I suggest you ( of course me ) to pay attention to this . --- pop --- ; pop two 4096(s) fxch st0,st3 ; st0 ,st1 = 4096 fcompp ; pop st0,st1 --- pop ---Next we calculate "x_i^2" --- calculate perm 1 ---
; we have st0 = x, st1 = y
; load x
fld st0
; x^2
fmul st0 ; x^2
--- calculate perm 1 ---
--- calculate 2 ---
; load y
fld st2
; y - x_i^2
fsubrp st1
--- calculate 2 ---
--- calculate 3 ---
;( y - x ^ 2 ) ^ 2
fmul st0
;100 * ( y - x ^ 2 ) ^ 2
fimul word [ para1 ]
--- calculate 3 ---
--- calculate 4 ---
; 1 - x
fld1
fsub st2
; ( 1 - x ) ^ 2
fmul st0
--- calculate 4 ---
--- calculate 5 ---
;{ 100 * ( y - x ^ 2 ) ^ 2 + ( 1 - x ) ^ 2 }
faddp st1
--- calculate 5 ---
Do the same manner, when we calculate '{ 100 * ( x - y ^ 2 ) ^ 2 + ( 1 - y ) ^ 2 }'. Code is following, but I don't explain it anymore.
--- calculate 6 ---
; load y
fld st2
; y^2
fmul st0 ; y_i^2
; load x
fld st2
; x - y^2
fsubrp st1
;( y - x ^ 2 ) ^ 2
fmul st0
;100 * ( y - x ^ 2 ) ^ 2
fimul word [ para1 ]
; 1 - x
fld1
fsub st3
; ( 1 - x ) ^ 2
fmul st0
;{ 100 * ( y - x ^ 2 ) ^ 2 + ( 1 - x ) ^ 2 }
faddp st1
--- calculate 6 ---
So we finish calculating perms. Finally, we add them. And store it to [si].
--- sum up & store --- ; store fitness to [si] fstp dword [si] --- sum up & store ---When we have stored the fitness value. But we have 2 value in FP stack . So we pop them ,and return . --- finalize --- fcompp ret --- finalize --- SelectionHey, fitness value is finished. We must select the adam and eve with fitness value. ( hmm if they didn't eat apple, they must not select with fitness :) We shoot she and he with roulette .Roulette Selection Algorithm.Before start this section.I change little bit ,but it's important. By now, I used the size of gene with 4, it's constant. This isn't good . When we change the genesize, all 4 turn to new size in source code. So I make it CONSTANT .--- gene size --- GENESIZE equ 4 --- gene size ---If you want to change size of gene, only change this line. All line will be affected, WHEN you RECOMPILE it . ( it's often occured for me, source is updated, but execute isn't updated ... ) Let's get back to the feature ! I choose the roulette selection. But there are various selection algo. I don't know why I choose it :). But I choose it . Tactics is following umm But, much of debuggind time brought me some fatal bug... If following routine is made, great value is occupied wide range seat. Luckily, I found solution. To take a reciprocal number . And always before calculatition, we take a reciprocal number . No other change is needed. But this leave us big problem ,if we choose this solution forever, we always must change this routine. In future ( isn't so long ), I'll standardize it to something. This is flow of my routine. Seeing is believing. --- roulette selection initialize --- selection: ; Roulette selection ; initialize fldz ; total mov di,fitness ; start address of fitness value mov cx,POPULATION ; number of POPULATION --- roulette selection initialize ---Umm, initializing routine is quite quiet. 'fldz' loads 0 to st(0) . --- roulette selection 1 --- ; calculation total fitness rouletteloop1: fld1 ; 0.092 fld dword [di] ; load fitness fdivp st1 ; 0.092 faddp st1 ; sum up fitness add di,4 ; to point next fitness loop rouletteloop1 --- roulette selection 1 ---2 line have its comment with '0.092'. This is fixed version to calculate a reciprocal. This loop is looped by POPULATION times.And when loop is finished, st0 have total. So I explain about 'fld1', 'fld dword [di]', 'fdivp st1', and 'faddp st1'. Before we start this, we initialized FPU STACK with 'fldz'.So st(0) = 0.0 (after 'fldz') --- stack --- before 'fld1' - st(0) = 0 'fld1' - st(0) = 1, st(1) = 0 'fld dword [di]' - st(0) = gene(0)'s fitness value, st(1) = 1 ,st(2) = 0 'fdivp st1' - st(0) = a reciprocal of gene(0)'s fitness ,st(1) = 0 'faddp st1' - st(0) = a reciprocal of gene(0)'s fitness --- stack ---st(0) 's value ( this time was 0 ) push to st(1), and st(0) have gene(0)'s fitness value, indicated by [di] .
Next 'faddp st1' is done .
Then di = di + 4, so [di] indicates next genes fitness value.And loop . --- roulette selecion 2 ---
; roulette start
call random ; generate random
mov [ randtmp ], ax ; save random to temp
fild dword [ randtmp ] ; load random number from temp
fild dword [ randmax ] ; load maximum number of random
fdivp st1 ; st1 / st0 ( random / maximum ), this
; make 0 < random < 1
fmul st1 ; 0 < target < total fitness
--- roulette selecion 2 ---
this formula is all : target = ( random number / maximum random number ) * totalBy doing this ,we know where is target .So we can choose adam. --- FPU STACK is something like ... ---
call random - st(0) = total fitness
mov [ randtmp ], ax - st(0) = total fitness
fild dword [ randtmp ] - st(0) = random number, st(1) = total fitness
fild dword [ randmax ] - st(0) = RANDOM MAX , st(1) = random number,
st(2) = total fitness
fdivp st1 - st(0) = 0 < random < 1, st(1) = total fitness
fmul st1 - st(0) = 0 < random < total fitness, st(1) = total fitness
--- FPU STACK is something like ... ---
Next we have some initialization.
--- roulette selection before3 --- ; seek lucky adam mov cx,POPULATION ; number of POPULATION mov si,randnum - GENESIZE ; start address of gene pool - GENESIZE (1 previous) mov di,fitness - 4 ; start address of fitness value - 4 (1 previous) --- roulette selection before3 ---Why [ - GENESIZE ] ? Why [ - 4 ] ? We compare the chanpion ( current minimum ) and target ( canditate ) . At first Following code will do, add GENESIZE or 4. To do this, next indivisual is target canditate. So if we set " randnum " only, the first indivisual isn't investigated . --- roulette selection 3 - 4 ---
rouletteloop2:
add si, GENESIZE ; next gene
add di,4 ; next fitness
fld1 ; 0.092
fld dword [di] ; load fitness
fdivp st1 ; 0.092
fcom st1 ;
fnstsw ax ; during st0 < st1 ,ah = ???????1.
; if st0 > st1 ah = ???????0
and ah,1 ; check condition
fsubp st1,st0 ; substract st1 - st0 and result store
; in st1 but 1 pop occur st0 have finally result
loopnz rouletteloop2 ; cx = cx - 1. if cx isn't 0 but zeroflag is clear,
; exit loop
--- roulette selection 3 - 4 ---
Firstly, increment canditate. and calculate a reciprocal of its fitness( fld1 ... fdivp ).And compare with total (fcom st1 ). Next line 'fnstsw ax' is tricky. This is load FPU STATUS into 'AX' . As comment say, we only need 1 flag ( of FPU STATUS ), which is at bit 0 of ah. After loading FPU STATUS into AX, we check bit 0 of ah . With "and ah,1" If bit 0 of ah is clear(0), zero flag is clear . If bit 0 of ah is set(1), zero flag is set. Next (fsubp ...) is total = total - fitness. By doing this ,total down to 0 . And if total is enough small, someone's fitness is grater than total.He is ADAM :) We met adam, so we want him to meet eve under the apple tree . So we pick up him from pool ( he can't swim well ) --- roulette selection 5 --- ; now [ si ] is adam's gene mov di, parent1 ; here's seat for him mov cx, GENESIZE ; to copy gene, cx have genesize (byte) rep movsb ; do copy fcomp st1 ; adios adam --- roulette selection 5 ---We copy GENESIZE ( bytes). 'rep movsb' do: [ di ] = [ si ]. di = di + 1, si = si + 1. (exactlly di and si is affected flag...but now I don't explain it ) This is a half of way .But another half is same. Only diffrence is before 'ret' isn't 'fcomp st1' . The line before 'ret' is 'fcompp' ( 2 stack is poped ) Making offspring The main stream is 1. select parents 2. crossover choosen parents 3. go to 1 until next generation is full. I don't want cx and di to destroy. So I use push and pop to save them. The code will explain by itself :) --- maker --- ; making offspring loop ; init mov cx, POPULATION ; loop counter for next generation mov di, offspring ; next swimmers warming up room makeoffspring: push cx push di call selection ; selecti parents by ROULETTe alGo pop di call crossover ; corssover by uniform crossover add di, GENESIZE ; next position pop cx loop makeoffspring --- maker ---Where is 'crossover' ?, Next ! Crossover We have choosen both parents ( sometimes parent... ) --- crossover --- ; uniform crossover ; parent1, parent2 is gene ; di = offsprring ( destination address ) ; broken: ; ax,bx,cx,dx --- crossover ---Umm, these are comments. --- crossover 1 ---
crossover:
xor bx,bx ; indicator ( counter ) reset
--- crossover 1 ---
bx is used for indicator which we are treating and also store the offspring.
--- crossover 1.5 --- crossover0: mov cx,8 ;1 byte is 8 bit. --- crossover 1.5 ---Why cx is 8 ? caz 1 byte is 8 bits. So we can loop whole a byte. --- crossover 2 ---
crossover1:
call random ; generate random number
shr al,1 ; check even or odd
jc crossover2 ; if odd (ie carry flag is set ), take parent2
mov al, [ parent1 + bx ] ; because even, take parent1
jmp crossover3 ; go to set
crossover2:
mov al, [ parent2 + bx ] ; take parent2
crossover3:
--- crossover 2 ---
First call random, and check the number is odd or even.
The probability is half and half. If odd we take parent2, so jump.
If even, we take parent1 .And pass through parent2 routine.
--- crossover 3 --- shl al,cl ; shift al left by cl times rcr dl,1 ; rotate dl including carry flag by 1 --- crossover 3 ---This is something tricky for non-assembler experience users. First shift al ( which have parent we choose ) left cl times . So al's ( 8 - cx ) bit go to carry flag. Second rotate dl 1 times to right, according to this dl's 7 bit is same carry flag. --- crossover 4 --- loop crossover1 ; cx = cx -1 --- crossover 4 ---This pattern is repeated 8 times ( 1 byte is 8 bit ) --- crossover 5 ---
mov [di + bx],dl ; save it as gene
inc bx
cmp bx, GENESIZE
jnz crossover0
--- crossover 5 ---
After looping, we have made 1 byte. So we store it as offspring's gene.
And counter is increased. If counter( this case 'bx' ) isn't GENESIZE, loop back to crossover0.
--- crossover 6 --- ret --- crossover 6 ---Bye bye crossover routine! MutationFirstly we decide how often mutation occurs. I take 0.001 for this. I think it's good that rate is between 0 to 1 ( 0 =< rate =< 1 )--- mutationrate --- mutationrate: dd 0.001 ; mutation rate --- mutationrate ---Secondly, we initialize for mutation routine. Almost of initialization routine is as same as crossover. --- mutation initialize ---
; mutation
; di = offspring start address
mutation:
xor bx,bx
mutation0:
mov cx,8 ;1 byte is 8 bit.
--- mutation initialize ---
Next, we want rancom number between 0 to 1 .
Remember we made this routine before.So copy and paste.
And modify it for this routine.
And to compare, we must load mutationrate .
--- mutation 1 --- call random ; generate random mov [ randtmp ], ax ; save temp fld dword[ mutationrate ] ; load mutationrate fild dword [ randtmp ] ; load random number from temp fild dword [ randmax ] ; load maximum number of random fdivp st1 ; st1 / st0, this make 0 < random < 1 --- mutation 1 ---Next we compare mutationrate and random number. If mutationrate > random number then mutation occur. If mutationrate < random number then mutation does not occur. well if mutationrate = random number ?, hehe homework :) --- stack --- fld dword[ mutationrate ] - st(0) = mutation rate fild dword [ randtmp ] - st(0) = random number, st(1) = mutation rate fild dword [ randmax ] - st(0) = RAND_MAX, st(1) = random number st(2) = mutation rate fdivp st1 - st(0) = random number / RNAD_MAX, st(1) = mutaion rate --- stack --- --- mutation 2 ---
fcompp ; compare and pop st0 ,st1
fnstsw ax ; during st0 < st1 ,ah = ???????1. if st0 >
; st1 ah = ???????0
shr ah,1 ; bit 0 of ah -> carry flag
rcr dl,1 ; save carry flag
loop mutation1 ; do this 1 byte( ie 8 times )
--- mutation 2 ---
Flag condtion is at ah's bit 0. So we did 'shr ah,1', carry flag have condition.
And 'rcr dl', 'dl' have condtion .
If there is mutaion, 'dl' have '1' on that bit.
--- mutation 3 ---
xor [ di + bx ], dl ; Do MUTATION
--- mutation 3 ---
So we know where is mutation when we investigate with 'dl'.
'xor' make the mutation .
--- mutation 4 ---
inc bx ; next position
cmp bx,GENESIZE ; it's end?
jnz mutation0 ; if it's not end, we go back to 'mutation0'.
--- mutation 4 ---
Do these routines until whole gene.
MainLoopFinally, we make all element of GA .So We make main loop . The main loop is most important factor which is when GA is end . But this time, I decide to loop 10000 times, if the best optimal solution is found. Of course, you can implement the main loop is end when the best solution is found.--- mainloop 1 ---
; mainloop start
mov cx, 10000 ; main loop counter
mainloop:
push cx ; save main loop counter
--- mainloop 1 ---
Why 'push cx' ?, caz 'cx' might be destroy during loop.So we save it.
--- mainloop 2 ---
; calculate all fitness
mov cx, POPULATION
mov di, randnum
mov si, fitness
loop2:
call evaluate
add di, GENESIZE ; ver 0.06
add si,4
loop loop2
--- mainloop 2 ---
Each gene's fitness is calculated and stored.
--- mainloop 3 ---
; making offspring loop
; init
mov cx, POPULATION ; loop counter for next generation
mov di, offspring ; next swimmer warming up room
makeoffspring:
push cx
push di
call selection ; selection by ROULETTe alGo
pop di
call crossover ; corssover by uniform crossover
call mutation ; mutation
add di, GENESIZE ; next offspring's position
pop cx
loop makeoffspring
--- mainloop 3 ---
Make next generation.
--- mainloop 4 ---
; judgement gene reach to goal ?
pop cx ; load loop counter
push cx ; save again
cmp cx,1 ; if the loop is end this time
jnz changepool ; if the loop is continued, go to next.
call judgement ; search best fitness
--- mainloop 4 ---
Once we load counter and check it if this time is end .
Umm, this is dirty way... But now, I remain it.
If loop is continued, we go to next .
If loop is end this time, we search best fitness.
--- mainloop 5 ---
; change gene to new.
changepool:
mov di, randnum ; old gene pool
mov si, offspring ; new gene are here.
mov cx, POPULATION * GENESIZE ; size of pool
rep movsb ; Do change
--- mainloop 5 ---
We have made new gene pool, and change old pool with new one.
--- mainloop 6 ---
pop cx ; counter is back
loop mainlooop ; loop
--- mainloop 6 ---
This is the entire mainloop routine.
But where is 'judgement' routine ? Follow me !
JudgementThese code is used for judgement. Usually, judgement routine check whether any solution satisfies end condition . But this doesn't check it. Only search minimum solution .--- judgement initialize ---
; judgement
; this version is to search minimum value of fitness.
; return
; si = best gene address
; dx = best fitness value address
judgement:
mov bx, randnum ; bx = start address of gene
mov si,bx ; si = bx
mov di, fitness ; dx = start address of fitness value
mov dx,di ; di = dx
mov cx, POPULATION - 1 ; cx is loop counter
fld dword [ di ] ; first fitness is loaded
--- judgement initialize ---
Above section is initialized routine.
--- judgement 1 ---
judgement1:
add bx, GENESIZE ; next gene
add di, 4 ; next fitness
fld dword [ di ] ; load fitness value
fcom st1 ; compare st0( current ) and st1 ( minimum )
fnstsw ax ; st0 < st1 ,ah = ???????1. st0 >
; st1 ah = ???????0
shr ah,1 ; check ah
jnc judgement2
mov dx,di ; save address of fitness value
mov si,bx ; save address of gene
fxch st0,st1 ; exchange minimum value
judgement2:
fcomp st1 ; poped
loop judgement1
fcomp st1 ; poped
ret
--- judgement 1 ---
Tactics is following This is all. Conclusion?For the present, we could make GA system.But there are many of possibility. Something, single point mutation, elitism, island model etc... If you have something interesting, I'm glad.Future Plan(Now there is a lot of hope for optimization)Let's enjoy coding!
Submitted: 27/02/2001 Article content copyright © Manabu Ishii, 2001.
|
|
|||||||||||||
All content copyright © 1998-2007, Generation5 unless otherwise noted.
- Privacy Policy - Legal - Terms of Use -