⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lgados.bas

📁 An Introduction to Genetic Algorithms for Scientists and Engineers By David Coley, World Scientific
💻 BAS
字号:
'-----------------------------------------------------------------------
'LGADOS - A DOS based version of the LGA Genetic Algorithm.

'For Distribution with the book "An Introduction to Genetic Algorithms for
'Scientists and Engineers", World Scientific 1998.

'David A. Coley
'Complex Systems Group
'Physics Building
'University of Exeter
'Exeter
'EX4 4QL
'UK
'email D.A.Coley@exeter.ac.uk

'Please email comments and corrections to D.A.Coley@exeter.ac.uk

'Version = 17th July 1998

'Before using this software please check for updates and corrections at
'http://www.ex.ac.uk/cee/ga


'-----------------------------------------------------------------------


'------- DECLARE ALL THE SUBROUTINES (PROCEDURES) USED BY THE PROGRAM -------

DECLARE SUB OpenFiles ()
DECLARE SUB Scaling (ScalingConstant!, FittestIndividual!, SumFitness!, MeanFitness!)
DECLARE SUB Elite (SumFitness!, FittestIndividual!)
DECLARE SUB Selection (mate!, SumFitness!, MeanFitness!)
DECLARE SUB CrossOver (Mate1!, Mate2!, NewIndividual!)
DECLARE SUB FindFitness ()
DECLARE SUB PrintGeneration (Generation, MeanFitness!, FittestIndividual!)
DECLARE SUB DefineRange ()
DECLARE SUB FindIntegers ()
DECLARE SUB FindUnknowns ()
DECLARE SUB InitialPopulation ()
DECLARE SUB NoCrossover (Mate1!, Mate2!, NewIndividual!)
DECLARE SUB Mutate ()
DECLARE SUB Replace ()
DECLARE SUB Statistics (MeanFitness!, SumFitness!, FittestIndividual!, Generation)

'------- SET ALL THE IMPORTANT FIXED PARAMETERS. -------

'These should be set by the user.
CONST PopulationSize = 20 'Must be even.
CONST NumberOfUnknowns = 2
CONST SubstringLength = 12 'All sub-strings have the same length.
CONST TotalStringLength = NumberOfUnknowns * SubstringLength
CONST MaxGeneration = 20 'G.
CONST CrossOverProbability = .6  'Pc. >=0 and <=1.
CONST MutationProbability = 1 / TotalStringLength  'Pm, >=0 and <1.
CONST Elitism = "on" '"on" or "off".
CONST ScalingConstant = 1.2 'A value of 0 implies no scaling.


'------DECLARE ALL SHARED (IE. GLOBAL) VARIABLES----------

'The arrays that hold the individuals within the current population.
DIM SHARED Unknowns(PopulationSize, NumberOfUnknowns) AS SINGLE
DIM SHARED Integers(PopulationSize, NumberOfUnknowns) AS LONG
DIM SHARED Strings(PopulationSize, TotalStringLength) AS INTEGER
DIM SHARED Fitness(PopulationSize) AS SINGLE

'The new population.
DIM SHARED NewStrings(PopulationSize, TotalStringLength) AS INTEGER

'The array that defines the range of the unknowns.
DIM SHARED Range(2, NumberOfUnknowns) AS SINGLE

'The best individual in the past generation. Used if elitism is on.
DIM SHARED EliteString(TotalStringLength) AS INTEGER
DIM SHARED EliteIntegers(NumberOfUnknowns) AS LONG
DIM SHARED EliteFitness AS SINGLE
DIM SHARED EliteUnknowns(NumberOfUnknowns) AS SINGLE

CLS  'Clear the screen.

CALL DefineRange  'Define the range of each unknown. These should also be set by the user.

'Set the random number generator so it produces a different set of numbers
'each time LGADOS is run.
RANDOMIZE TIMER

CALL OpenFiles  'Open files used to store results.

'------- START OF THE GENETIC ALGORITHM -------

'------- CREATE AN INITIAL POPULATION, GENERATION 1 ------

Generation = 1

CALL InitialPopulation  'Build a population of strings at random.

CALL FindFitness  'Find the fitness of each member of the population.

CALL Statistics(MeanFitness, SumFitness, FittestIndividual, Generation)  'Find the mean fitness and the fittest individual.

CALL PrintGeneration(Generation, MeanFitness, FittestIndividual)  'Print generation to file.

CALL Scaling(ScalingConstant, FittestIndividual, SumFitness, MeanFitness)   'If linear fitness scaling is "on" then scale population prior to selection.

'------- LOOP OVER ALL THE GENERATIONS -------

FOR Generation = 2 TO MaxGeneration

  FOR NewIndividual = 1 TO PopulationSize STEP 2  'Loop over the population choosing pairs of mates.

    CALL Selection(Mate1, SumFitness, MeanFitness)  'Choose first mate.
    CALL Selection(Mate2, SumFitness, MeanFitness)  'Choose second mate.
                                                       
    'Pass individuals to the temporary population either with or without performing crossover.
    IF RND <= CrossOverProbability THEN  'Perform crossover.
      CALL CrossOver(Mate1, Mate2, NewIndividual)
    ELSE 'Don't perform crossover.
      CALL NoCrossover(Mate1, Mate2, NewIndividual)  'Don't perform crossover.
    END IF

  NEXT NewIndividual

  CALL Mutate  'Mutate the temporary population.

  CALL Replace  'Replace the old population completely by the new one.

  CALL FindUnknowns  'De-code the new population to integers then real numbers.

  CALL FindFitness  'Find the fitness of each member of the population.

  CALL Statistics(MeanFitness, SumFitness, FittestIndividual, Generation)  'Find the mean fitness and the fittest individual.
        
  CALL PrintGeneration(Generation, MeanFitness, FittestIndividual)  'Print generation to file.

  CALL Scaling(ScalingConstant, FittestIndividual, SumFitness, MeanFitness)  'If linear fitness scaling is "on" then scale population prior to selection.

NEXT Generation  'Process the next generation.

CLOSE  'Close all files

SUB CrossOver (Mate1, Mate2, NewIndividual)
'Perform single point crossover.

CrossSite = INT((TotalStringLength - 1) * RND + 1) 'Pick the cross-site at random.

FOR bit = 1 TO CrossSite 'Swap bits to the left of the cross-site.
  NewStrings(NewIndividual, bit) = Strings(Mate1, bit)
  NewStrings(NewIndividual + 1, bit) = Strings(Mate2, bit)
NEXT bit

FOR bit = CrossSite + 1 TO TotalStringLength 'Swap bits to the right of the cross-site.
  NewStrings(NewIndividual, bit) = Strings(Mate2, bit)
  NewStrings(NewIndividual + 1, bit) = Strings(Mate1, bit)
NEXT bit

END SUB

SUB DefineRange
'Defines the upper and lower bounds of each unknown.
'Add other ranges using the same format if more than two unknowns in the problem.

Unknown = 1 'the first unknown.
Range(1, Unknown) = 0 'The lower bound.
Range(2, Unknown) = 1 'The upper bound.

Unknown = 2 'the second unknown.
Range(1, Unknown) = -3.14159
Range(2, Unknown) = 3.14159

'Add other ranges if more than two unknowns in your problem.

END SUB

SUB Elite (SumFitness, FittestIndividual)
'Applies elitism by replacing a randomly chosen individual by the elite member
'from the previous population if the new max fitness is less then the previous value.

IF Fitness(FittestIndividual) < EliteFitness THEN

  Individual = INT(PopulationSize * RND + 1) 'Chosen individual to be replaced.

  FOR bit = 1 TO TotalStringLength
    Strings(Individual, bit) = EliteString(bit)
  NEXT bit

  Fitness(Individual) = EliteFitness

  FOR Unknown = 1 TO NumberOfUnknowns
    Integers(Individual, Unknown) = EliteIntegers(Unknown)
    Unknowns(Individual, Unknown) = EliteUnknowns(Unknown)
  NEXT Unknown

  FittestIndividual = Individual

END IF

FOR bit = 1 TO TotalStringLength
        EliteString(bit) = Strings(FittestIndividual, bit)
NEXT bit

EliteFitness = Fitness(FittestIndividual)
                      
FOR Unknown = 1 TO NumberOfUnknowns
  EliteIntegers(Unknown) = Integers(FittestIndividual, Unknown)
  EliteUnknowns(Unknown) = Unknowns(FittestIndividual, Unknown)
NEXT Unknown

END SUB

SUB FindFitness
'The problem at hand is used to assign a positive (or zero) fitness to each individual in turn.

'The problem is f = x^2 + sin(y).
FOR Individual = 1 TO PopulationSize
  Fitness(Individual) = Unknowns(Individual, 1) ^ 2 + SIN(Unknowns(Individual, 2))
  IF Fitness(Individual) < 0 THEN Fitness(Individual) = 0
NEXT Individual
        
END SUB

SUB FindIntegers
'Decode the strings to sets of decimal integers.

DIM bit AS INTEGER

FOR Individual = 1 TO PopulationSize

  bit = TotalStringLength + 1
  FOR Unknown = NumberOfUnknowns TO 1 STEP -1

    Integers(Individual, Unknown) = 0
    FOR StringBit = 1 TO SubstringLength

      bit = bit - 1
      IF Strings(Individual, bit) = 1 THEN
        Integers(Individual, Unknown) = Integers(Individual, Unknown) + 2 ^ (StringBit - 1)
      END IF

    NEXT StringBit

  NEXT Unknown

NEXT Individual
    
END SUB

SUB FindUnknowns
'Decode the strings to real numbers.

CALL FindIntegers 'First decode the strings to sets of decimal integers.

'Now convert these integers to reals.
FOR Individual = 1 TO PopulationSize
  FOR Unknown = 1 TO NumberOfUnknowns
    Unknowns(Individual, Unknown) = Range(1, Unknown) + Integers(Individual, Unknown) * (Range(2, Unknown) - Range(1, Unknown)) / (2 ^ SubstringLength - 1)
  NEXT Unknown
NEXT Individual
   
END SUB

SUB InitialPopulation
'Create the initial random population.

FOR Individual = 1 TO PopulationSize

  FOR bit = 1 TO TotalStringLength
    IF RND > .5 THEN
      Strings(Individual, bit) = 1
    ELSE
      Strings(Individual, bit) = 0
    END IF
  NEXT bit
        
NEXT Individual

CALL FindUnknowns 'Decode strings to real numbers.

END SUB

SUB Mutate
'Visit each bit of each string very occasionally flipping a "1" to a "0" or vice versa.

FOR Individual = 1 TO PopulationSize
  FOR bit = 1 TO TotalStringLength
    
    'Throw a random number and see if it is less than or equal to the mutation probability.
    IF RND <= MutationProbability THEN
           
      'Mutate.
      IF NewStrings(Individual, bit) = 1 THEN
              NewStrings(Individual, bit) = 0
      ELSE
              NewStrings(Individual, bit) = 1
      END IF

    END IF
                 
  NEXT bit

NEXT Individual
             
END SUB

SUB NoCrossover (Mate1, Mate2, NewIndividual)
'Pass the selected strings to the temporary population without applying crossover.

FOR bit = 1 TO TotalStringLength
  NewStrings(NewIndividual, bit) = Strings(Mate1, bit)
  NewStrings(NewIndividual + 1, bit) = Strings(Mate2, bit)
NEXT bit

END SUB

SUB OpenFiles
'Open result files. See Chapter 2 for a description of their contents.

OPEN "LGADOS.RES" FOR OUTPUT AS #1
OPEN "LGADOS.ALL" FOR OUTPUT AS #2

END SUB

SUB PrintGeneration (Generation, MeanFitness, FittestIndividual)
'Print results to the screen and the files.

PRINT Generation; Fitness(FittestIndividual); MeanFitness;  'Screen.
PRINT #1, Generation; ","; Fitness(FittestIndividual); ","; MeanFitness; 'File LGADOS.RES.

FOR Unknown = 1 TO NumberOfUnknowns
  PRINT Unknowns(FittestIndividual, Unknown); 'Screen.
  PRINT #1, ","; Unknowns(FittestIndividual, Unknown); ","; 'File LGADOS.RES
NEXT Unknown

PRINT 'Carriage return.
PRINT #1, 'Carriage return.

FOR Individual = 1 TO PopulationSize
 
  PRINT #2, Generation; ","; Fitness(Individual); ","; 'File LGADOS.ALL

  FOR Unknown = 1 TO NumberOfUnknowns
    PRINT #2, Unknowns(Individual, Unknown); ","; 'File LGADOS.ALL
  NEXT Unknown
 
  FOR bit = 1 TO TotalStringLength
    PRINT #2, RIGHT$(STR$(Strings(Individual, bit)), 1); ","; 'File LGADOS.ALL
  NEXT bit
 
  PRINT #2, 'Carriage return

NEXT Individual

END SUB

SUB Replace
'Replace the old population with the new one.

FOR Individual = 1 TO PopulationSize
  FOR bit = 1 TO TotalStringLength
    Strings(Individual, bit) = NewStrings(Individual, bit)
  NEXT bit
NEXT Individual

ERASE NewStrings 'Clear the old array of strings.

END SUB

SUB Scaling (ScalingConstant, FittestIndividual, SumFitness, MeanFitness)

'Apply Linear Fitness Scaling,
'      scaledfitness = a * fitness + b.
'Subject to,
'      meanscaledfitness = meanfitness
'and
'      bestscaledfitness = c * meanfitness,
'where c, the scaling constant, is set by the user.

'If the scaling constant is set to zero, or all individuals have the same
'fitness, scaling is not applied.
IF ScalingConstant <> 0 AND Fitness(FittestIndividual) - MeanFitness > 0 THEN
  'Find a and b.

  a = (ScalingConstant - 1) * MeanFitness / (Fitness(FittestIndividual) - MeanFitness)

  b = (1 - a) * MeanFitness

  'Adjust the fitness of all members of the population.
  SumFitness = 0
  FOR Individual = 1 TO PopulationSize
    Fitness(Individual) = a * Fitness(Individual) + b
    IF Fitness(Individual) < 0 THEN Fitness(Individual) = 0 'Avoid negative values near the end of a run.
    SumFitness = SumFitness + Fitness(Individual) 'Adjust the sum of all the fitnesses.
  NEXT Individual

  'Adjust the mean of all the fitnesses.
  MeanFitness = SumFitness / PopulationSize
END IF

END SUB

SUB Selection (mate, SumFitness, MeanFitness)
'Select a single individual by fitness proportional selection.

Sum = 0
Individual = 0

RouletteWheel = RND * SumFitness

DO
  Individual = Individual + 1
  Sum = Sum + Fitness(Individual)
LOOP UNTIL Sum >= RouletteWheel OR Individual = PopulationSize

mate = Individual

END SUB

SUB Statistics (MeanFitness, SumFitness, FittestIndividual, Generation)
'Calculate the sum of fitness across the population and find the best individual,
'then apply elitism if required.

FittestIndividual = 0
MaxFitness = 0

FOR Individual = 1 TO PopulationSize
  IF Fitness(Individual) > MaxFitness THEN
    MaxFitness = Fitness(Individual)
    FittestIndividual = Individual
  END IF
NEXT Individual

IF Elitism = "on" THEN 'Apply elitism.
  CALL Elite(SumFitness, FittestIndividual)
END IF

SumFitness = 0 'Sum the fitness.
FOR Individual = 1 TO PopulationSize
  SumFitness = SumFitness + Fitness(Individual)
NEXT Individual

'Find the average fitness of the population.
MeanFitness = SumFitness / PopulationSize

END SUB

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -