¿Cómo creo una lista de números aleatorios sin duplicados?


110

Intenté usar random.randint(0, 100), pero algunos números eran los mismos. ¿Existe un método / módulo para crear una lista de números aleatorios únicos?

Nota: El siguiente código se basa en una respuesta y se agregó después de que se publicó la respuesta. No es parte de la pregunta; es la solucion.

def getScores():
    # open files to read and write
    f1 = open("page.txt", "r");
    p1 = open("pgRes.txt", "a");

    gScores = [];
    bScores = [];
    yScores = [];

    # run 50 tests of 40 random queries to implement "bootstrapping" method 
    for i in range(50):
        # get 40 random queries from the 50
        lines = random.sample(f1.readlines(), 40);

1
Si son únicos, pueden ser verdaderamente aleatorios en el contexto correcto. Como una muestra aleatoria de índices sin reemplazo, todavía puede ser completamente aleatorio.
gbtimmon

Respuestas:


180

Esto devolverá una lista de 10 números seleccionados del rango de 0 a 99, sin duplicados.

import random
random.sample(range(100), 10)

Con referencia a su ejemplo de código específico, probablemente desee leer todas las líneas del archivo una vez y luego seleccionar líneas aleatorias de la lista guardada en la memoria. Por ejemplo:

all_lines = f1.readlines()
for i in range(50):
    lines = random.sample(all_lines, 40)

De esta manera, solo necesita leer el archivo una vez, antes de su ciclo. Es mucho más eficiente hacer esto que volver al inicio del archivo y f1.readlines()volver a llamar para cada iteración del ciclo.


2
Esta técnica desperdicia memoria, especialmente para muestras grandes. Publiqué código para una solución mucho más eficiente de memoria y computación a continuación que utiliza un generador congruencial lineal.
Thomas Lux

Sin embargo, se me señaló que el método LCG es menos "aleatorio", por lo que si desea generar muchas secuencias aleatorias únicas, la variedad será menor que esta solución. Si solo necesita un puñado de secuencias aleatorias, ¡LCG es el camino a seguir!
Thomas Lux

Gracias Greg, fue útil
N Sivaram

15

Puede usar la función de reproducción aleatoria del módulo aleatorio de esta manera:

import random

my_list = list(xrange(1,100)) # list of integers from 1 to 99
                              # adjust this boundaries to fit your needs
random.shuffle(my_list)
print my_list # <- List of unique random numbers

Tenga en cuenta aquí que el método shuffle no devuelve ninguna lista como cabría esperar, solo baraja la lista pasada por referencia.


Es bueno mencionar aquí que xrange solo funciona en Python 2 y no en Python 3.
Shayan Shafiq

10

Primero puede crear una lista de números desde ahasta b, donde ay bson respectivamente los números más pequeños y más grandes en su lista, luego mezclarlos con el algoritmo de Fisher-Yates o usando el random.shufflemétodo de Python .


1
Generar una lista completa de índices es una pérdida de memoria, especialmente para muestras grandes. Publiqué código para una solución mucho más eficiente de memoria y computación a continuación que utiliza un generador congruencial lineal.
Thomas Lux

8

La solución presentada en esta respuesta funciona, pero podría volverse problemática con la memoria si el tamaño de la muestra es pequeño, pero la población es enorme (por ejemplo random.sample(insanelyLargeNumber, 10)).

Para arreglar eso, iría con esto:

answer = set()
sampleSize = 10
answerSize = 0

while answerSize < sampleSize:
    r = random.randint(0,100)
    if r not in answer:
        answerSize += 1
        answer.add(r)

# answer now contains 10 unique, random integers from 0.. 100

Ahora random.sampleusa este enfoque para una pequeña cantidad de muestras de una gran población, por lo que este problema con la memoria ya no existe. Aunque, en el momento en que se escribió esta respuesta, la implementación de random.shufflepuede haber sido diferente.
kyrill

5

Generador de números pseudoaleatorios congruentes lineales

O (1) Memoria

O (k) Operaciones

Este problema se puede resolver con un simple generador de congruencia lineal . Esto requiere una sobrecarga de memoria constante (8 enteros) y como máximo 2 * cálculos (longitud de secuencia).

Todas las demás soluciones usan más memoria y más computación. Si solo necesita unas pocas secuencias aleatorias, este método será significativamente más económico. Para rangos de tamaño N, si desea generar en el orden de secuencias Núnicas ko más, recomiendo la solución aceptada utilizando los métodos incorporados, random.sample(range(N),k)ya que se ha optimizado en Python para la velocidad.

Código

# Return a randomized "range" using a Linear Congruential Generator
# to produce the number sequence. Parameters are the same as for 
# python builtin "range".
#   Memory  -- storage for 8 integers, regardless of parameters.
#   Compute -- at most 2*"maximum" steps required to generate sequence.
#
def random_range(start, stop=None, step=None):
    import random, math
    # Set a default values the same way "range" does.
    if (stop == None): start, stop = 0, start
    if (step == None): step = 1
    # Use a mapping to convert a standard range into the desired range.
    mapping = lambda i: (i*step) + start
    # Compute the number of numbers in this range.
    maximum = (stop - start) // step
    # Seed range with a random integer.
    value = random.randint(0,maximum)
    # 
    # Construct an offset, multiplier, and modulus for a linear
    # congruential generator. These generators are cyclic and
    # non-repeating when they maintain the properties:
    # 
    #   1) "modulus" and "offset" are relatively prime.
    #   2) ["multiplier" - 1] is divisible by all prime factors of "modulus".
    #   3) ["multiplier" - 1] is divisible by 4 if "modulus" is divisible by 4.
    # 
    offset = random.randint(0,maximum) * 2 + 1      # Pick a random odd-valued offset.
    multiplier = 4*(maximum//4) + 1                 # Pick a multiplier 1 greater than a multiple of 4.
    modulus = int(2**math.ceil(math.log2(maximum))) # Pick a modulus just big enough to generate all numbers (power of 2).
    # Track how many random numbers have been returned.
    found = 0
    while found < maximum:
        # If this is a valid value, yield it in generator fashion.
        if value < maximum:
            found += 1
            yield mapping(value)
        # Calculate the next value in the sequence.
        value = (value*multiplier + offset) % modulus

Uso

El uso de esta función "rango_aleatorio" es el mismo que para cualquier generador (como "rango"). Un ejemplo:

# Show off random range.
print()
for v in range(3,6):
    v = 2**v
    l = list(random_range(v))
    print("Need",v,"found",len(set(l)),"(min,max)",(min(l),max(l)))
    print("",l)
    print()

Resultados de muestra

Required 8 cycles to generate a sequence of 8 values.
Need 8 found 8 (min,max) (0, 7)
 [1, 0, 7, 6, 5, 4, 3, 2]

Required 16 cycles to generate a sequence of 9 values.
Need 9 found 9 (min,max) (0, 8)
 [3, 5, 8, 7, 2, 6, 0, 1, 4]

Required 16 cycles to generate a sequence of 16 values.
Need 16 found 16 (min,max) (0, 15)
 [5, 14, 11, 8, 3, 2, 13, 1, 0, 6, 9, 4, 7, 12, 10, 15]

Required 32 cycles to generate a sequence of 17 values.
Need 17 found 17 (min,max) (0, 16)
 [12, 6, 16, 15, 10, 3, 14, 5, 11, 13, 0, 1, 4, 8, 7, 2, ...]

Required 32 cycles to generate a sequence of 32 values.
Need 32 found 32 (min,max) (0, 31)
 [19, 15, 1, 6, 10, 7, 0, 28, 23, 24, 31, 17, 22, 20, 9, ...]

Required 64 cycles to generate a sequence of 33 values.
Need 33 found 33 (min,max) (0, 32)
 [11, 13, 0, 8, 2, 9, 27, 6, 29, 16, 15, 10, 3, 14, 5, 24, ...]

1
¡Esto es muy genial! Pero estoy seguro de que realmente responde a la pregunta; digamos que quiero muestrear 2 valores de 0 a 4. Sin generar el mío prime, la función solo me devolverá 4 respuestas posibles, porque valuees la única cosa elegida al azar con 4 valores posibles, cuando necesitamos al menos (4 elegir 2) = 6, (permitiendo un orden no aleatorio). random_range(2,4)devolverá los valores {(1, 0), (3, 2), (2, 1), (0, 3)}, pero nunca el par (3,1) (o (1,3)). ¿Está esperando nuevos números primos grandes generados aleatoriamente en cada llamada de función?
wowserx

1
(También supongo que espera que las personas mezclen la secuencia después de que su función la devuelva si quieren un orden aleatorio, ya que random_range(v)regresa a vsecuencias únicas en lugar de v!)
wowserx

¡Totalmente cierto! Es difícil equilibrar entre evitar el desbordamiento de enteros y generar suficientes secuencias aleatorias. Actualicé la función para incorporar un poco más de aleatoriedad, ¡pero aún no es tan aleatorio como v !. Depende de si desea utilizar la función varias veces. Esta solución se utiliza mejor cuando se genera a partir de una amplia gama de valores (cuando el consumo de memoria de otros sería mucho mayor). Lo pensaré más, ¡gracias!
Thomas Lux

4

Si la lista de N números del 1 al N se genera aleatoriamente, entonces sí, existe la posibilidad de que algunos números se repitan.

Si desea una lista de números del 1 al N en un orden aleatorio, complete una matriz con números enteros del 1 al N y luego use una combinación de Fisher-Yates o Python random.shuffle().


3

Si necesita muestrear números extremadamente grandes, no puede usar range

random.sample(range(10000000000000000000000000000000), 10)

porque arroja:

OverflowError: Python int too large to convert to C ssize_t

Además, si random.sampleno puede producir la cantidad de elementos que desea debido a que el rango es demasiado pequeño

 random.sample(range(2), 1000)

arroja:

 ValueError: Sample larger than population

Esta función resuelve ambos problemas:

import random

def random_sample(count, start, stop, step=1):
    def gen_random():
        while True:
            yield random.randrange(start, stop, step)

    def gen_n_unique(source, n):
        seen = set()
        seenadd = seen.add
        for i in (i for i in source() if i not in seen and not seenadd(i)):
            yield i
            if len(seen) == n:
                break

    return [i for i in gen_n_unique(gen_random,
                                    min(count, int(abs(stop - start) / abs(step))))]

Uso con números extremadamente grandes:

print('\n'.join(map(str, random_sample(10, 2, 10000000000000000000000000000000))))

Resultado de la muestra:

7822019936001013053229712669368
6289033704329783896566642145909
2473484300603494430244265004275
5842266362922067540967510912174
6775107889200427514968714189847
9674137095837778645652621150351
9969632214348349234653730196586
1397846105816635294077965449171
3911263633583030536971422042360
9864578596169364050929858013943

Uso donde el rango es menor que el número de elementos solicitados:

print(', '.join(map(str, random_sample(100000, 0, 3))))

Resultado de la muestra:

2, 0, 1

También funciona con rangos y pasos negativos:

print(', '.join(map(str, random_sample(10, 10, -10, -2))))
print(', '.join(map(str, random_sample(10, 5, -5, -2))))

Resultados de muestra:

2, -8, 6, -2, -4, 0, 4, 10, -6, 8
-3, 1, 5, -1, 3

¿y si usted es generar más de 8 mil millones números, más pronto o más tarde se convertirá visto demasiado grande
david_adler

Esta respuesta tiene un defecto grave para muestras grandes. La probabilidad de colisión crece linealmente con cada paso. Publiqué una solución utilizando un generador congruencial lineal que tiene O (1) sobrecarga de memoria y O (k) pasos necesarios para generar k números. ¡Esto se puede resolver de manera mucho más eficiente!
Thomas Lux

¡Esta respuesta es definitivamente mejor si desea generar una cantidad de secuencias aleatorias en el orden de la longitud de la secuencia! El método LCG es menos "aleatorio" cuando se trata de generar múltiples secuencias únicas.
Thomas Lux

"Esta función resuelve ambos problemas" ¿Cómo resuelve el segundo problema? Aún no puede tomar 1000 muestras de una población de 2. En lugar de lanzar una excepción, produce un resultado incorrecto; eso difícilmente es una resolución del "problema" (que en realidad no es un problema para empezar, ya que no es del todo razonable solicitar k muestras únicas de una población de n <k ).
kyrill

1

Puede usar la biblioteca Numpy para una respuesta rápida como se muestra a continuación:

El fragmento de código dado enumera 6 números únicos entre el rango de 0 a 5. Puede ajustar los parámetros para su comodidad.

import numpy as np
import random
a = np.linspace( 0, 5, 6 )
random.shuffle(a)
print(a)

Salida

[ 2.  1.  5.  3.  4.  0.]

No impone ninguna restricción como vemos en random.sample como se hace referencia aquí .

Espero que esto ayude un poco.


1

La respuesta proporcionada aquí funciona muy bien con respecto al tiempo y a la memoria, pero un poco más complicada ya que utiliza construcciones de Python avanzadas como yield. La respuesta más simple funciona bien en la práctica, pero el problema con esa respuesta es que puede generar muchos números enteros falsos antes de construir el conjunto requerido. Pruébelo con PopulationSize = 1000, sampleSize = 999. En teoría, existe la posibilidad de que no termine.

La respuesta a continuación aborda ambos problemas, ya que es determinista y algo eficiente, aunque actualmente no es tan eficiente como los otros dos.

def randomSample(populationSize, sampleSize):
  populationStr = str(populationSize)
  dTree, samples = {}, []
  for i in range(sampleSize):
    val, dTree = getElem(populationStr, dTree, '')
    samples.append(int(val))
  return samples, dTree

donde las funciones getElem, percolateUp son las que se definen a continuación

import random

def getElem(populationStr, dTree, key):
  msd  = int(populationStr[0])
  if not key in dTree.keys():
    dTree[key] = range(msd + 1)
  idx = random.randint(0, len(dTree[key]) - 1)
  key = key +  str(dTree[key][idx])
  if len(populationStr) == 1:
    dTree[key[:-1]].pop(idx)
    return key, (percolateUp(dTree, key[:-1]))
  newPopulation = populationStr[1:]
  if int(key[-1]) != msd:
    newPopulation = str(10**(len(newPopulation)) - 1)
  return getElem(newPopulation, dTree, key)

def percolateUp(dTree, key):
  while (dTree[key] == []):
    dTree[key[:-1]].remove( int(key[-1]) )
    key = key[:-1]
  return dTree

Finalmente, el tiempo en promedio fue de aproximadamente 15 ms para un valor grande de n como se muestra a continuación,

In [3]: n = 10000000000000000000000000000000

In [4]: %time l,t = randomSample(n, 5)
Wall time: 15 ms

In [5]: l
Out[5]:
[10000000000000000000000000000000L,
 5731058186417515132221063394952L,
 85813091721736310254927217189L,
 6349042316505875821781301073204L,
 2356846126709988590164624736328L]

¿Crees que la respuesta es complicada? ¡¿Qué es esto entonces ?! Y luego está la otra respuesta , que genera muchos "números enteros falsos". Ejecuté su implementación con la entrada de ejemplo que proporcionó (tamaño de población = 1000, tamaño de muestra = 999). Su versión llama a la random.randintfunción 3996 veces, mientras que la otra cca. 6000 veces. No es una gran mejora, ¿eh?
kyrill

@kyrill, tu opinión sobre esta respuesta
aak318

1

Para obtener un programa que genere una lista de valores aleatorios sin duplicados que sea determinista, eficiente y construido con construcciones de programación básicas, considere la función extractSamplesdefinida a continuación,

def extractSamples(populationSize, sampleSize, intervalLst) :
    import random
    if (sampleSize > populationSize) :
        raise ValueError("sampleSize = "+str(sampleSize) +" > populationSize (= " + str(populationSize) + ")")
    samples = []
    while (len(samples) < sampleSize) :
        i = random.randint(0, (len(intervalLst)-1))
        (a,b) = intervalLst[i]
        sample = random.randint(a,b)
        if (a==b) :
            intervalLst.pop(i)
        elif (a == sample) : # shorten beginning of interval                                                                                                                                           
            intervalLst[i] = (sample+1, b)
        elif ( sample == b) : # shorten interval end                                                                                                                                                   
            intervalLst[i] = (a, sample - 1)
        else :
            intervalLst[i] = (a, sample - 1)
            intervalLst.append((sample+1, b))
        samples.append(sample)
    return samples

La idea básica es realizar un seguimiento de los intervalos intervalLstde posibles valores entre los que seleccionar nuestros elementos requeridos. Esto es determinista en el sentido de que tenemos la garantía de generar una muestra dentro de un número fijo de pasos (dependiente únicamente de populationSizey sampleSize).

Para usar la función anterior para generar nuestra lista requerida,

In [3]: populationSize, sampleSize = 10**17, 10**5

In [4]: %time lst1 = extractSamples(populationSize, sampleSize, [(0, populationSize-1)])
CPU times: user 289 ms, sys: 9.96 ms, total: 299 ms
Wall time: 293 ms

También podemos comparar con una solución anterior (para un valor más bajo de PopulationSize)

In [5]: populationSize, sampleSize = 10**8, 10**5

In [6]: %time lst = random.sample(range(populationSize), sampleSize)
CPU times: user 1.89 s, sys: 299 ms, total: 2.19 s
Wall time: 2.18 s

In [7]: %time lst1 = extractSamples(populationSize, sampleSize, [(0, populationSize-1)])
CPU times: user 449 ms, sys: 8.92 ms, total: 458 ms
Wall time: 442 ms

Tenga en cuenta que reduje el populationSizevalor ya que produce un error de memoria para valores más altos al usar la random.samplesolución (también mencionado en las respuestas anteriores aquí y aquí ). Para los valores anteriores, también podemos observar que extractSamplessupera el random.sampleenfoque.

PD: Aunque el enfoque principal es similar a mi respuesta anterior , hay modificaciones sustanciales en la implementación, así como en el enfoque junto con una mejora en la claridad.


0

Una función muy sencilla que también resuelve tu problema

from random import randint

data = []

def unique_rand(inicial, limit, total):

        data = []

        i = 0

        while i < total:
            number = randint(inicial, limit)
            if number not in data:
                data.append(number)
                i += 1

        return data


data = unique_rand(1, 60, 6)

print(data)


"""

prints something like 

[34, 45, 2, 36, 25, 32]

"""

0

El problema con los enfoques basados ​​en conjuntos ("si hay un valor aleatorio en los valores devueltos, inténtalo de nuevo") es que su tiempo de ejecución no está determinado debido a las colisiones (que requieren otra iteración de "intentar de nuevo"), especialmente cuando se devuelve una gran cantidad de valores aleatorios. de la gama.

Una alternativa que no es propensa a este tiempo de ejecución no determinista es la siguiente:

import bisect
import random

def fast_sample(low, high, num):
    """ Samples :param num: integer numbers in range of
        [:param low:, :param high:) without replacement
        by maintaining a list of ranges of values that
        are permitted.

        This list of ranges is used to map a random number
        of a contiguous a range (`r_n`) to a permissible
        number `r` (from `ranges`).
    """
    ranges = [high]
    high_ = high - 1
    while len(ranges) - 1 < num:
        # generate a random number from an ever decreasing
        # contiguous range (which we'll map to the true
        # random number).
        # consider an example with low=0, high=10,
        # part way through this loop with:
        #
        # ranges = [0, 2, 3, 7, 9, 10]
        #
        # r_n :-> r
        #   0 :-> 1
        #   1 :-> 4
        #   2 :-> 5
        #   3 :-> 6
        #   4 :-> 8
        r_n = random.randint(low, high_)
        range_index = bisect.bisect_left(ranges, r_n)
        r = r_n + range_index
        for i in xrange(range_index, len(ranges)):
            if ranges[i] <= r:
                # as many "gaps" we iterate over, as much
                # is the true random value (`r`) shifted.
                r = r_n + i + 1
            elif ranges[i] > r_n:
                break
        # mark `r` as another "gap" of the original
        # [low, high) range.
        ranges.insert(i, r)
        # Fewer values possible.
        high_ -= 1
    # `ranges` happens to contain the result.
    return ranges[:-1]

0
import random

sourcelist=[]
resultlist=[]

for x in range(100):
    sourcelist.append(x)

for y in sourcelist:
    resultlist.insert(random.randint(0,len(resultlist)),y)

print (resultlist)

1
Bienvenido a Stackoverflow. Explique su respuesta por qué y cómo resuelve el problema para que otros puedan entender su respuesta fácilmente.
octobus

Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación y probablemente resultaría en más votos a favor. Recuerde que está respondiendo la pregunta a los lectores en el futuro, no solo a la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos. De la opinión
doble pitido

-1

Si desea asegurarse de que los números que se agregan sean únicos, puede usar un objeto Set

si usa 2.7 o superior, o importe el módulo de conjuntos si no.

Como han mencionado otros, esto significa que los números no son verdaderamente aleatorios.


-1

para muestrear enteros sin reemplazo entre minvaly maxval:

import numpy as np

minval, maxval, n_samples = -50, 50, 10
generator = np.random.default_rng(seed=0)
samples = generator.permutation(np.arange(minval, maxval))[:n_samples]

# or, if minval is 0,
samples = generator.permutation(maxval)[:n_samples]

con jax:

import jax

minval, maxval, n_samples = -50, 50, 10
key = jax.random.PRNGKey(seed=0)
samples = jax.random.shuffle(key, jax.numpy.arange(minval, maxval))[:n_samples]

¿Por qué generaría una permutaiton de un número posiblemente grande de elementos y luego solo seleccionaría el primero n_samplesde ellos? ¿Cuál es su razonamiento detrás de este enfoque? ¿Puede explicar cuáles son las ventajas de su enfoque, en comparación con cualquiera de la gran cantidad de respuestas existentes (la mayoría de hace 8 años)?
kyrill

en realidad, mi respuesta tiene una complejidad similar a la de otras respuestas más votadas y es más rápida porque usa numpy. otros métodos, los más votados random.shuffle, utilizan Mersenne Twister, que es mucho más lento que los algoritmos ofrecidos por numpy (y probablemente jax). numpy y jax permiten otros algoritmos de generación de números aleatorios. jax también permite la compilación jit y la diferenciación, lo que puede ser útil para la diferenciación estocástica. Además, con respecto a una matriz "posiblemente grande", algunas de las respuestas más votadas hacen exactamente lo mismo random.shuffle, lo que no creo que sea pecaminoso en un sentido relativo o incluso absoluto
grisaitis

1
No estoy seguro de lo que quiere decir con " random.shuffleusa Mersenne Twister": es la mezcla de Fisher-Yates, como se menciona en varias respuestas. Tiene una complejidad de tiempo lineal, por lo que no puede ser asintóticamente más lento que los algoritmos ofrecidos por cualquier otra biblioteca, ya sea numérica o de otro tipo. Si numpy es más rápido, es solo porque está implementado en C, pero esto no justifica generar una gran permutación (una que ni siquiera cabe en la memoria), solo para elegir algunos elementos de ella. No hay una sola respuesta además de la tuya que haga esto.
kyrill

Mis disculpas, leí que Python usó al azar Mersenne Twister ya que es prng. ¿Tiene una fuente para que pueda aprender más sobre Fisher Yates y el papel en random.shuffle?
grisaitis

Ya hay dos enlaces separados a Wikipedia en dos respuestas separadas aquí. Si Wikipedia no es una fuente suficientemente buena para usted, hay 14 referencias al final del artículo. Y luego está Google. ¿Eso ayuda? Ah, y el randommódulo está escrito en Python, por lo que puede ver fácilmente su fuente (probar random.__file__).
kyrill

-3

Desde la CLI en win xp:

python -c "import random; print(sorted(set([random.randint(6,49) for i in range(7)]))[:6])"

En Canadá tenemos la lotería 6/49. Simplemente envuelvo el código anterior en lotto.bat y ejecuto C:\home\lotto.bato simplemente C:\home\lotto.

Debido a que a random.randintmenudo repite un número, lo uso setcon range(7)y luego lo acorto a una longitud de 6.

Ocasionalmente, si un número se repite más de 2 veces, la longitud de la lista resultante será inferior a 6.

EDITAR: Sin embargo, random.sample(range(6,49),6)es el camino correcto a seguir.


-3
import random
result=[]
for i in range(1,50):
    rng=random.randint(1,20)
    result.append(rng)

1
¿Podría explicar cómo esto evita duplicados? No es obvio a partir de este volcado de código.
Toby Speight

No es así. print len(result), len(set(result)). Es de esperar que resulttenga elementos únicos solo una vez en cada 1.0851831788708547256608362340568947172111832359638926... × 10^20intento.
Jedi
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.