2011-12-01 16 views
6

En lugar de un completo shuffle, estoy buscando un parcialshuffle función en python.¿Cómo hacer una mezcla aleatoria pero parcial en Python?

Ejemplo: "cadena" debe dar lugar a "stnrig", pero no "nrsgit"

Sería mejor si puedo definir un "porcentaje" específico de caracteres que tienen que ser reorganizado.

propósito es probar algoritmos de comparación de cadenas. Quiero determinar el "porcentaje de mezcla" más allá del cual un (mi) algoritmo marcará dos cadenas (barajadas) como completamente diferentes.

Actualización:

Aquí está mi código. ¡Las mejoras son bienvenidas!

import random 

percent_to_shuffle = int(raw_input("Give the percent value to shuffle : ")) 
to_shuffle = list(raw_input("Give the string to be shuffled : ")) 

num_of_chars_to_shuffle = int((len(to_shuffle)*percent_to_shuffle)/100) 

for i in range(0,num_of_chars_to_shuffle): 
    x=random.randint(0,(len(to_shuffle)-1)) 
    y=random.randint(0,(len(to_shuffle)-1)) 
    z=to_shuffle[x] 
    to_shuffle[x]=to_shuffle[y] 
    to_shuffle[y]=z 

print ''.join(to_shuffle) 
+0

el problema con su código de barajar es que podría terminar con menos caracteres mezclados de los deseados si hay una secuencia de swaps que hace un ciclo ... – fortran

+0

sí, es bastante posible para cadenas pequeñas. Creo que mi código está sesgado hacia la velocidad en lugar de la precisión. – 384X21

+0

algunos otros consejos: ¿por qué estás incrementando 'i' al final del ciclo? no debería tener ningún efecto (creo que es un remanente de una versión 'while'); el intercambio es más idiomático en Python con la deconstrucción de la tupla en lugar de usar variables intermedias. – fortran

Respuesta

2

Su problema es complicado, porque hay algunos casos extremos en que pensar sobre: ​​

  • cadenas con caracteres repetidos (es decir, ¿cómo barajar "AAAAB"?)
  • ¿Cómo se miden los intercambios de caracteres encadenados o la reorganización de bloques?

En cualquier caso, es probable que la métrica definida para mezclar cadenas hasta cierto porcentaje sea la misma que está usando en su algoritmo para ver qué tan cerca están.

Mi código para barajar n caracteres:

import random 
def shuffle_n(s, n): 
    idx = range(len(s)) 
    random.shuffle(idx) 
    idx = idx[:n] 
    mapping = dict((idx[i], idx[i-1]) for i in range(n)) 
    return ''.join(s[mapping.get(x,x)] for x in range(len(s))) 

elige Básicamente n posiciones para cambiar al azar, y luego los intercambios cada uno de ellos con el siguiente en la lista ... De esta manera se asegura que no hay permutas inversas se generan y exactamente se cambian los caracteres n (si hay caracteres repetidos, mala suerte).

explicó correr con 'cadena', 3 como entrada:

idx is [0, 1, 2, 3, 4, 5] 
we shuffle it, now it is [5, 3, 1, 4, 0, 2] 
we take just the first 3 elements, now it is [5, 3, 1] 
those are the characters that we are going to swap 
s t r i n g 
^^^
t (1) will be i (3) 
i (3) will be g (5) 
g (5) will be t (1) 
the rest will remain unchanged 
so we get 'sirgnt' 

Lo malo de este método es que no genera todas las posibles variaciones, por ejemplo, no podría hacer 'gnrits' de 'cuerda'.Esto se podría solucionar haciendo particiones de los índices que se barajan, así:

import random 

def randparts(l): 
    n = len(l) 
    s = random.randint(0, n-1) + 1 
    if s >= 2 and n - s >= 2: # the split makes two valid parts 
     yield l[:s] 
     for p in randparts(l[s:]): 
      yield p 
    else: # the split would make a single cycle 
     yield l 

def shuffle_n(s, n): 
    idx = range(len(s)) 
    random.shuffle(idx) 
    mapping = dict((x[i], x[i-1]) 
     for i in range(len(x)) 
     for x in randparts(idx[:n])) 
    return ''.join(s[mapping.get(x,x)] for x in range(len(s))) 
1

quizá de este modo:

>>> s = 'string' 
>>> shufflethis = list(s[2:]) 
>>> random.shuffle(shufflethis) 
>>> s[:2]+''.join(shufflethis) 
'stingr' 

Tomando de la idea de Fortran, estoy añadiendo a esta colección. Es bastante rápido:

def partial_shuffle(st, p=20): 
    p = int(round(p/100.0*len(st))) 

    idx = range(len(s)) 
    sample = random.sample(idx, p) 

    res=str() 
    samptrav = 1 

    for i in range(len(st)): 
     if i in sample: 
      res += st[sample[-samptrav]] 
      samptrav += 1 
      continue 
     res += st[i] 

    return res 
+1

Esto mezclará la misma parte de la cadena cada vez. – DrTyrsa

3

Este es un problema más simple de lo que parece. Y el lenguaje tiene las herramientas adecuadas no permanecer entre usted y la idea, como de costumbre:

import random 

def pashuffle(string, perc=10): 
    data = list(string) 
    for index, letter in enumerate(data): 
     if random.randrange(0, 100) < perc/2: 
      new_index = random.randrange(0, len(data)) 
      data[index], data[new_index] = data[new_index], data[index] 
    return "".join(data) 
+3

Guau, su código es tan reutilizable y limpio, aunque no resuelve la tarea. – DrTyrsa

+0

¿Y cómo esto "no resuelve la tarea", cuando lo hace? – jsbueno

+0

¿Y cómo puede mezclar toda la cadena con 'perc = 50'? – DrTyrsa

1
import random 

def partial_shuffle(a, part=0.5): 
    # which characters are to be shuffled: 
    idx_todo = random.sample(xrange(len(a)), int(len(a) * part)) 

    # what are the new positions of these to-be-shuffled characters: 
    idx_target = idx_todo[:] 
    random.shuffle(idx_target) 

    # map all "normal" character positions {0:0, 1:1, 2:2, ...} 
    mapper = dict((i, i) for i in xrange(len(a))) 

    # update with all shuffles in the string: {old_pos:new_pos, old_pos:new_pos, ...} 
    mapper.update(zip(idx_todo, idx_target)) 

    # use mapper to modify the string: 
    return ''.join(a[mapper[i]] for i in xrange(len(a))) 

for i in xrange(5): 
    print partial_shuffle('abcdefghijklmnopqrstuvwxyz', 0.2) 

impresiones

abcdefghljkvmnopqrstuxwiyz 
ajcdefghitklmnopqrsbuvwxyz 
abcdefhwijklmnopqrsguvtxyz 
aecdubghijklmnopqrstwvfxyz 
abjdefgcitklmnopqrshuvwxyz 
0

El mal y el uso de una API en desuso:

import random 
# adjust constant to taste 
# 0 -> no effect, 0.5 -> completely shuffled, 1.0 -> reversed 
# Of course this assumes your input is already sorted ;) 
''.join(sorted(
    'abcdefghijklmnopqrstuvwxyz', 
    cmp = lambda a, b: cmp(a, b) * (-1 if random.random() < 0.2 else 1) 
)) 
+0

Gracioso, pero ¿está garantizado que termine siempre? Si la función de comparación es inconsistente, creo que podría haber una secuencia de pasos de ordenamiento que podrían repetirse continuamente (dependiendo del algoritmo utilizado, la ruta rápida debería ser inmune a esto, por ejemplo). – fortran

Cuestiones relacionadas