Hamlet thinks that Polonius is a fool, which he demonstrates in the following conversation:
Hamlet: “Do you see yonder cloud that’s almost in shape of a camel?"
Polonius: "By the mass, and ‘tis like a camel, indeed."
Hamlet: "Methinks it is like a weasel."
Polonius: "It is backed like a weasel."
Hamlet: "Or like a whale?"
Polonius: "Very like a whale.”
You will be very familiar with Richard Dawkins (ethologist and evolutionary biologist) and his expositions of evolution which are always completely proof against any counter-argument. In one of his books "The Blind Watchmaker" he describes his attempts to demonstrate natural selection by mutation and survival of the fittest by creating a computer program. See the Wikipedia URL for detailed information.
https://en.wikipedia.org/wiki/Weasel_programHis computer program simulates an analogous process by attempting to create the phrase "Methinks It Is Like A Weasel" from a starting string block of random letters. At some point in each of our lives there is probably a desire to write our own "Methinks It Is Like A Weasel" program, so this is my attempt (program updated 15/8/19 for more realistic probabilities, error removal and tidied).
'METHINKS IT IS LIKE A WEASEL by QWERKEY 15/8/19
'128 Males + 128 Females
'128*Birthrate Male Offspring + 128*Birthrate Female Offspring: Male Odd-recessive, Female Even-recessive
'A certain percentage of mutations in the offspring
'Only the most adapted 128 Male and 128 Female Offspring survive
'As Methinks2 (tidied up) but with immovable correct gene removed, mutation rate much reduced, and errors corrected
CONST True
= -1, False
= 0 CONST NoMates%
= 128, NoIssue%
= 141, JumpStart%
= 256, MutationRate!
= 2 / 1000 CONST Elite!
= 3 / 7, Kappa!
= 4, Prob0!
= 0.23, GenLimit%
= 30000 DIM Mates$
(NoMates%
- 1, 1), MatesDat%
(NoMates%
- 1, 1), IssueDat%
(NoIssue%
- 1, 1) DIM Mating%%
(NoMates%
- 1)
_TITLE "Methinks It Is Like A Weasel" Weasel$ = "METHINKS[IT[IS[LIKE[A[WEASEL"
Alphabet$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ["
'Initialise 1st Generation
Generation% = 1
FOR N%%
= 0 TO NoMates%
- 1 Mates$
(N%%
, R%%
) = MID$(Alphabet$
, INT(RND * 26) + 1, 1) Mates$
(N%%
, R%%
) = Mates$
(N%%
, R%%
) + MID$(Alphabet$
, INT(RND * 27) + 1, 1) MatesDat%(N%%, R%%) = 0
MatesDat%
(N%%
, R%%
) = MatesDat%
(N%%
, R%%
) + ABS(ASC(MID$(Weasel$
, M%%
, 1)) - ASC(MID$(Mates$
(N%%
, R%%
), M%%
, 1)))'Sort & Display 1st Generation
CALL Sort1
(MatesDat%
(), Mates$
())
'Cycle through generations:
REDIM Papa%%
(NoIssue%
- 1, 1) Dawkins%% = True
OneByOne%% = True
WHILE Dawkins%%
AND Generation%
< GenLimit%
'Display Current Generation
FOR N%%
= 0 TO NoMates%
- 1 W$ = Mates$(N%%, R%%) 'W$ gets modified by Spaced$()
_PRINTSTRING (4 + 250 + R%%
* 500, 15 + (N%%
- 64) * 11), Spaced$
(W$
) 'Find partners (monogamous, brother & sister not allowed to mate)
PartnersAvailable%% = False
PartnersAvailable%% = True
REDIM Mated%%
(NoMates%
- 1) FOR N%%
= 0 TO NoMates%
- 1 CanMate%% = False
NoTries% = 0
N1%%
= INT(RND * NoMates%
/ 2) NoTries% = NoTries% + 1
IF NOT Mated%%
(N1%%
) AND (Papa%%
(N%%
, 0) <> Papa%%
(N1%%
, 1) OR Generation%
= 1) THEN 'By this time, Papa%% has been re-ordered Mated%%(N1%%) = True
Mating%%(N%%) = N1%%
CanMate%% = True
CanMate%% = True
PartnersAvailable%% = False
'Produce Children (always 1 male & 1 female offspring at the same time)
REDIM Issue$
(NoIssue%
- 1, 1), Papa%%
(NoIssue%
- 1, 1) NoKidsLess1% = 0
N%% = 0
WHILE NoKidsLess1%
<= NoIssue%
- 1 IF RND <= (Prob0!
+ ((Prob0!
* N%%
* (1 - Kappa!
)) / ((NoMates%%
- 1) * Kappa!
))) THEN FOR R%%
= 0 TO 1 'Male/Female Offspring Issue$
(NoKidsLess1%
, R%%
) = MID$(Alphabet$
, INT(RND * 26) + 1, 1) Issue$
(NoKidsLess1%
, R%%
) = Issue$
(NoKidsLess1%
, R%%
) + MID$(Alphabet$
, INT(RND * 27) + 1, 1) Issue$
(NoKidsLess1%
, R%%
) = Issue$
(NoKidsLess1%
, R%%
) + MID$(Mates$
(N%%
, 0), M%%
, 1) 'From father Issue$
(NoKidsLess1%
, R%%
) = Issue$
(NoKidsLess1%
, R%%
) + MID$(Mates$
(Mating%%
(N%%
), 1), M%%
, 1) 'From mother IssueDat%(NoKidsLess1%, R%%) = 0
IssueDat%
(NoKidsLess1%
, R%%
) = IssueDat%
(NoKidsLess1%
, R%%
) + ABS(ASC(MID$(Weasel$
, M%%
, 1)) - ASC(MID$(Issue$
(NoKidsLess1%
, R%%
), M%%
, 1))) Papa%%(NoKidsLess1%, R%%) = N%%
NoKidsLess1% = NoKidsLess1% + 1
N%% = 0
N%% = N%% + 1
'Delay
'Display Childless Parents
FOR N%%
= 0 TO NoMates%
- 1 'N%% is the father Progeny%% = False
T% = 0 'T% is the child
IF Papa%%
(T%
, 0) = N%%
THEN Progeny%%
= True
T% = T% + 1
W$ = Mates$(N%%, 0) 'W$ gets modified by Spaced$() - don't actually need this substitution here, as updated below
N2%% = Mating%%(N%%)
W$ = Mates$(N2%%, 1) 'W$ gets modified by Spaced$() - don't actually need this substitution here, as updated below
_PRINTSTRING (4 + 250 + 500, 15 + (N2%%
- 64) * 11), Spaced$
(W$
) 'Use up keypresses & wait for keypress
SetAwhile%% = True
SetAwhile%% = False
SetAwhile%% = False
Dawkins%% = False
OneByOne%% = False
SetAwhile%% = False
OneByOne%% = True
Dawkins%% = False
' Order children & set new generation
CALL Sort2
(IssueDat%
(), Issue$
(), Papa%%
()) FOR N%%
= 0 TO NoMates%%
- 1 Mates$(N%%, R%%) = Issue$(N%%, R%%)
MatesDat%(N%%, R%%) = 0
MatesDat%
(N%%
, R%%
) = MatesDat%
(N%%
, R%%
) + ABS(ASC(MID$(Weasel$
, M%%
, 1)) - ASC(MID$(Mates$
(N%%
, R%%
), M%%
, 1))) Generation% = Generation% + 1
'IF NOT OneByOne%% THEN _DISPLAY
IF Generation%
= GenLimit%
THEN PRINT "Generation Limit Reached"
Spaced$ = X$
SUB Sort1
(Numbers%
(), Names$
()) Jump% = JumpStart%
Jump% = (Jump% - 1) \ 2
Finished% = False
Finished% = True
FOR Upper%
= 1 TO NoMates%
- Jump%
Lower% = Upper% + Jump%
IF Numbers%
(Upper%
- 1, R%%
) > Numbers%
(Lower%
- 1, R%%
) THEN SWAP Names$
(Upper%
- 1, R%%
), Names$
(Lower%
- 1, R%%
) SWAP Numbers%
(Upper%
- 1, R%%
), Numbers%
(Lower%
- 1, R%%
) Finished% = False
SUB Sort2
(Numbers%
(), Names$
(), WhosTheDaddy%%
()) Jump% = JumpStart%
Jump% = (Jump% - 1) \ 2
Finished% = False
Finished% = True
FOR Upper%
= 1 TO NoIssue%
- Jump%
Lower% = Upper% + Jump%
IF Numbers%
(Upper%
- 1, R%%
) > Numbers%
(Lower%
- 1, R%%
) THEN SWAP Names$
(Upper%
- 1, R%%
), Names$
(Lower%
- 1, R%%
) SWAP Numbers%
(Upper%
- 1, R%%
), Numbers%
(Lower%
- 1, R%%
) SWAP WhosTheDaddy%%
(Upper%
- 1, R%%
), WhosTheDaddy%%
(Lower%
- 1, R%%
) Finished% = False
The assumptions/conditions in the program are as follows:
- Each generation has 128 males and 128 females who mate monogamously with a birthrate of 2.2 children (on average!).
- Each male and female of the first generation starts with a "gene sequence" defined by a 28-character string of random letters from "A" to "Z" (capitals only) and the Space character.
- The fitness-for-use criterion is defined by how close each character is to same-position character in the phrase "METHINKS IT IS LIKE A WEASEL".
- Each child inherits half its "genes" from the father and half from the mother. Male parents are odd-numbered "gene-recessive", whilst female parents are even-numbered recessive. Thus if the "DNA" were three characters long and the father was XYX and the mother YZY, the child would be YYY. At each breeding, one male and one female offspring are produced. Male and female offspring have the same genes as each other, except for the mutations.
- At each stage there is a chance (0.2% in this case) of gene mutation where a random character is produced in place of the inherited character. This mutation rate is still much higher than in the biological case.
- Brother and sister may not mate (we are not only simulating superior genetics, but are also completely morally sound).
- The mate desirability is higher for the top half of the population. (There is a 4:3 probability of top-half males selecting top-half females).
- For each 128/128 male/female generation, the top father has a 4-times probability of reproduction compared to that of the bottom pair.
- Of the 141 male & female offspring, only the top 128 survive to the next generation.
Run the program as follows:
The display shows two groups of 128 parents, "males" in yellow and "females" in cyan, each in two columns. They are shown in fitness-for-use order (highest at the top). The 64 highest are in the left-hand column, with the 64 lowest at the right.
After a short delay, the parents who fail to breed successfully are shown darkened. The program will wait for a Spacebar press to proceed to the next generation. You can cycle through the generations in this way. You will see that the most poorly fit-for-use parents are most likely not to reproduce.
Evolution proceeds quite slowly and you will very soon want to speed things up. Pressing "f" instead of the Spacebar will then quickly cycle through the generations. Pressing "f" will revert to the one-by-one generation display. Esc at any time quits the program.
You will see that about 3000 generations are required to produce a population in which all 256 individuals have the desired gene sequence (this takes about 10 minutes). This is despite the fact that gene evolution is quite heavily biassed in favour of the desired result. This is because of the presence in the population of such a large gene diversity. You will also notice that the likelihood of gene changes falls as the population gets closer to the final result. This is because any mutation tends to be quickly bred out by the extant majority. In Dawkins's program, he obtains the final result in 47 generations. I believe that this is so because his had a simpler procedure and less overall gene diversity.
I apologise for the slight bias in favour of the male sex, both in the coding and in this discussion. No matter how we try, sexism is difficulty to battle.
Of course, Dawkins was quite aware that writing a program to head towards a known result does not describe how biology works, and the program here is a just-for-fun exercise.
This is my contribution to the demonstration of selection by fitness-for-use. Please contribute your own program if you care to, describing what assumptions are made and how many generations it takes to produce the final population result. Methinks it could be fun for you too.