python casuale con uno skew

3

Sto provando a creare un algoritmo di evoluzione molto semplice per un simulatore di creature, quello che mi piacerebbe avere è che entrambe le creature hanno un tratto e un livello di dominanza, entrambi rilevati da ints. la caratteristica del loro bambino sarà un numero casuale tra la caratteristica della creatura A e la caratteristica della creatura B, poi spostata verso il più dominante. Quindi se A ha un tratto di 5 e il predominio di 2 e B ha un tratto di 10 e un dominio di 7, l'inclinazione sarà di -5 in modo da inclinarsi di più verso B. Il loro bambino ha più probabilità di avere un tratto di 8 6. Esiste un buon modo per farlo? Lo visualizzo finendo così: A5-6--7 --- 8 ---- ----- 9 10B

Non riesco a capire quanto dovrebbe essere l'inclinazione fino a quando non sarò in grado di testare i risultati, quindi per il momento è una specie di arbitrario.

Grazie a tutti per aver trovato il tempo di aiutarmi.

    
posta EasilyBaffled 07.03.2013 - 22:14
fonte

5 risposte

1

la mia soluzione ti darà un'inclinazione più strong per le maggiori differenze di dominanza:
sta usando il fatto che alzare un valore tra 0 e 1 su una potenza positiva distorce il risultato all'interno dello stesso intervallo.
imposta la regolazione fine, come meglio credi.

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))

nota che in questa soluzione i tratti devono essere ordinati. (ad es. a_trait < b_trait)

40 esecuzioni consecutive hanno dato come risultato:
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

    
risposta data 08.03.2013 - 00:21
fonte
2

Da quello che vedo, hai bisogno di una selezione casuale ponderata, da range(parent_a.trait, parent_b.trait) .

Per distribuire i pesi linearmente , la cosa più semplice che puoi fare è costruire un intervallo di floats in modo che il sum() dell'intervallo sia 1.0 , e ogni passo rappresenterebbe una probabilità di scegliere quella fase.

Esempio:

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)]

Come ho detto sopra, la somma dei pesi dovrebbe essere uguale a uno:

assert sum(weights) == 1.

Quindi genera una somma cumulativa dei pesi:

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

Ora scegli un numero casuale e trova l'indice del numero più grande che è più piccolo di detto numero casuale:

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

Infine, usa lo skew come offset dal tratto con la dominanza più grande:

if parent_a.dominance > parent_b.dominance:
    trait = parent_a.dominance + skew
else:
    trait = parent_b.dominance + skew
    
risposta data 07.03.2013 - 23:15
fonte
2

Certo, è abbastanza semplice da fare.

Prendi la somma di entrambi i valori di dominanza e prendi un valore random.randrange() di questi.

Se quel valore è inferiore al valore di dominanza del genitore A, hai scelto quella caratteristica del genitore, altrimenti è la caratteristica genitore di B che hai scelto:

import random

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

In altre parole, è una selezione casuale ponderata molto semplice tra due opzioni.

Per il tuo esempio specifico, la somma delle dominanze è 9, quindi il valore randrange() è uno tra 0 e 8; se viene selezionato 0 o 1, viene selezionato il tratto del genitore A, se viene selezionato 2, 3, 4, 5, 6, 7 o 8, allora viene selezionato il tratto del genitore B.

Se invece stai parlando di scegliere un tratto su un intervallo dal valore tratto del genitore A fino al valore tratto del genitore B in cui i tratti sono trattati come un intervallo, allora i tuoi valori di dominanza sono usati per "tirare" un valore tratto verso uno o l'altro genitore.

In pratica si tratta di un tiro alla fune tra i genitori. Con il predominio bilanciato, il tratto scelto sarebbe, in media, sceso al valore medio dei due tratti. Ma con un genitore che domina l'altro, il valore della caratteristica ha selezionato "spostamenti" verso il genitore dominante.

Ciò si traduce in una possibilità che i valori al di sotto del punto medio vengano selezionati per il genitore A abbiano una possibilità di parentA.dominance in (parentA.dominance + parentB.dominance), mentre i valori sopra il punto medio hanno una possibilità di parentB.dominance in (parentA.dominance + parentB.dominance) di essere scelto.

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

Un rapido tratto di raccolta demo tra il genitore A e il genitore B utilizzando il calcolo precedente:

>>> 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
    
risposta data 07.03.2013 - 22:23
fonte
1

Se comprendo correttamente la tua domanda, vuoi una funzione di distribuzione di probabilità come in basso. L'asse y è il tuo valore di dominanza e l'asse x è il valore del tratto.

Per generare un valore in questa distribuzione, in pratica prendi l'area sotto la curva, 12,5 in questo caso. Scegli un numero casuale uniforme tra 0 e 12,5, riempi quella zona partendo dal lato sinistro e osserva dove cade sull'asse x. Ad esempio, supponi di aver scelto 4.5. L'area sotto la curva tra 5 e 8 è 4,5, quindi la caratteristica del tuo bambino è 8.

Se la mia matematica è giusta, data a come il valore compreso tra 0 e 12.5 hai scelto, d1 e d2 sono le rispettive dominanze e t1 e t2 sono i tratti rispettivi, i tuoi figli il tratto è dato da:

t1 + sqrt(2*a*(d2-d1)/(t2-t1))
    
risposta data 07.03.2013 - 23:46
fonte
0

Puoi farlo generando due numeri casuali.

Diciamo che hai tratto A. Il primo 'genitore' ha un valore di 2. Il secondo un valore di 8. Quindi vuoi un intervallo da 2 a 8. Normalmente dovresti generare un numero casuale compreso tra 0 e 1.0, e moltiplicalo per (8 - 2) e aggiungilo a 2, come:

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

È abbastanza semplice, ma non si evolve in una particolare direzione.

Invece, genera due numeri casuali. Diciamo che vuoi che l'8 (il valore alto) sia il tratto 'dominante'. Genera due valori casuali tra 0 e 1.0 e usa il massimo dei due quando si calcola il valore del nuovo bambino. Ciò ti 'orienta' verso l'8.

Se si desidera che il valore basso sia dominante, fare lo stesso, utilizzando il più basso dei due numeri generati casualmente.

    
risposta data 07.03.2013 - 22:23
fonte

Leggi altre domande sui tag