Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I am trying to create a very simple evolution algorithm for a creature simulator, what I would like to have is, both creatures have a trait and a dominance level, both noted by ints. their child's trait will be a random number between creature A's trait and creature B's trait then skewed to the more dominant. So if A has a trait of 5 and dominance of 2 and B has a trait of 10 and a dominance of 7, the skew will be -5 so it skews more towards B. Their child is more likely to have a trait of 8 than 6. Is there a good way to do this? I visualise it ending up like this: A5-6--7---8----9-----10B

I can't figure out how much the skew should be until I am able to test the results, so for the time being it's kind of arbitrary.

Thank you everyone for taking the time to help me.

share|improve this question
add comment

5 Answers

up vote 1 down vote accepted

my solution will give you stronger skew for bigger dominance differences:
it is using the fact that raising a value between 0 and 1 to a positive power will skew the result within the same range.
set the fine-tuning, as you see fit.

import random
a_trait = 5
a_dominance = 2
b_trait = 10
b_dominance = 7

target = random.random()
skew = abs(a_dominance - b_dominance) + 1
fine_tuning_multiplier = 0.3
skewed_target = target ** (skew * fine_tuning_multiplier)

trait_delta = abs(a_trait - b_trait)
target_trait = trait_delta * (1 - skewed_target)

if a_dominance > b_dominance:
  val = b_trait - target_trait
else:
  val = a_trait + target_trait

print "val:", int(round(val))

note that in this solution the traits must be sorted. (i.e. a_trait < b_trait)

40 consecutive runs resulted in:
8 8 9 8 9 10 6 7 7 10 8 8 9 9 10 9 9 7 10 6 10 7 10 9 10 10 7 8 8 7 10 5 6 6 9 9 10 10 6 6

share|improve this answer
add comment

You can do this by generating two random numbers.

Say you have trait A. The first 'parent' has a value of 2. The second a value of 8. You thus want a range of 2 to 8. Normally you'd generate a random number between 0 and 1.0, and multiply that by (8 - 2) and add it to 2, like:

N = (rand() * (8-2)) + 2;

Simple enough, but it doesnt trend in a particular direction.

Instead, generate two random numbers. Lets say you want the 8 (the high value) to be the 'dominant' trait. Generate two random values between 0 and 1.0, and use the highest of the two when calculating the new child's value. That will 'trend' you towards 8.

If you wanted the low value to be dominant, do the same, using the lower of the two randomly generated numbers.

share|improve this answer
add comment

Sure, that's quite simple to do.

Just take the sum of both dominance values and take a random.randrange() value of these.

If that value is lower than the dominance value of parent A, you picked that parent's trait, otherwise it's parent B's trait you picked:

import random

if random.randrange(parentA.dominance + parentB.dominance) < parentA.dominance:
    trait = parentA.trait
else:
    trait = parentB.trait

In other words, it's a very simple weighted random selection between two options.

For your specific example, the sum of the dominances is 9, so the randrange() value is one between 0 and 8; if 0 or 1 is picked, parent A's trait is selected, if 2, 3, 4, 5, 6, 7 or 8 is picked then parent B's trait is selected instead.

If instead you are talking about picking a trait on a range from parent A's trait value up to and including parent B's trait value where traits are treated as a range, then your dominance values are used to 'pull' a trait value towards one or the other parent.

It basically comes down to a tug-of-war between the parents. With balanced dominance, the trait picked would, on average, come down to the average value of the two traits. But with one parent dominating over the other, the trait value picked 'shifts' towards the dominating parent.

This translates in a chance that the values below the midpoint are picked for parent A have a chance of parentA.dominance in (parentA.dominance + parentB.dominance), while the values above the midpoint have a chance of parentB.dominance in (parentA.dominance + parentB.dominance) of being picked.

import random

pick = random.random()

# sort parents by trait; smallest trait first
parents = sorted((parentA, parentB), key=attrgetter('trait'))
average = (parents[0].trait + parents[1].trait) / 2.0
weights = []
slots = (parents[1].trait - parents[0].trait + 1.0) * (parents[0].dominance + parents[1].dominance)
for i in range(parents[0].trait, parents[1].trait + 1):
    if i <= average:
        weights.append(sum(weights) + (parents[0].dominance / slots))
    else:
        weights.append(sum(weights) + (parents[1].dominance / slots))
    if weights[-1] > pick:
        return i

A quick demo picking traits between parent A and parent B using the above calculation:

>>> for i in range(40): print pickTrait(),
... 
9 9 9 10 6 9 8 10 7 9 9 8 8 8 9 8 9 9 7 5 8 8 9 5 9 9 10 10 10 10 5 5 9 9 8 9 10 7 8 9
share|improve this answer
    
I may have not phrased my question clearly enough, unfortunatly this is not the whole answer, I am not just taking A's trait or B's trait I am taking a number from within the range of their two values. and that random number will be dependent on the difference between the two parents dominance. –  EasilyBaffled Mar 7 '13 at 21:37
    
@EasilyBaffled: Well, what does it mean to have a trait of 5 more than a trait of 10? Are traits a range, are they expressible as floating point values, or are they discreet (e.g. 5 is something completely different than 6, etc.)? You can 'overlay' the weights of the parents onto a range of values between 5 and 10 (inclusive) but you need to be more specific if that has any meaning at all. –  Martijn Pieters Mar 7 '13 at 21:40
    
@MartijnPieters I think the OP simply wants a weighted random integer from randrange of [parentA.trait:parentB.trait], but weighted by the dominance (I assume a linear weight distribution). –  Attila O. Mar 7 '13 at 21:44
    
@AttilaO.: Perhaps, that is what I am trying to assertain. I'd like to hear that from the OP however. –  Martijn Pieters Mar 7 '13 at 21:45
    
@MartijnPieters traits of the parents are assigned randomly from a range of ints from 2 to 10. So 5 is average 10 is the best. Dominance is decided from a relation to the trait, which is why B has a higher dominance than A. If you want something more applicable, think of hight. I my dad is tall(6'3") my mom is short(5'1") I am 5'8" I am closer to my mom's hight, so it would stand to reason that her genes were dominant. Attila did a good job of summarizing it. –  EasilyBaffled Mar 7 '13 at 22:04
show 1 more comment

From what I see, you need a weighted random selection, from range(parent_a.trait, parent_b.trait).

To distribute the weights linearly, the simplest thing you could do is construct a range of floats in such a way that the sum() of the range is 1.0, and each step would represent a probability of choosing that one step.

Example:

trait_delta = abs(pacent_a.trait - trait_b.trait)
trait_delta_sum = trait_delta * (trait_delta+1.) / 2.
weights = [x/trait_delta_sum for x in range(1, trait_delta+1)]

As I said above, the sum of the weights should be equal to one:

assert sum(weights) == 1.

Then generate a cumulative sum of the weights:

cumulative_weihts = [sum(weights[:x]) for x in range(trait_delta)]

Now just pick a random number, and find the index of the largest number that is smaller than said random number:

rand = random.random()
shift = sum[-1 for x in cumulative_weightn if r<x])

Finally, use the skew as an offset from the trait with the bigger dominance:

if parent_a.dominance > parent_b.dominance:
    trait = parent_a.dominance + skew
else:
    trait = parent_b.dominance + skew
share|improve this answer
add comment

If I understand your question correctly, you want a probability distribution function somewhat like below. The y axis is your dominance value and the x axis is the trait value.

linear probability distribution

To generate a value in this distribution, you basically take the area under the curve, 12.5 in this case. Choose a uniform random number between 0 and 12.5, fill in that much area starting from the left side, and see where it falls on the x axis. For example, say you chose 4.5. The area under the curve between 5 and 8 is 4.5, so your child's trait is 8.

If my math is right, given a as the value between 0 and 12.5 you chose, d1 and d2 are the respective dominances, and t1 and t2 are the respective traits, your child's trait is given by:

t1 + sqrt(2*a*(d2-d1)/(t2-t1))
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.