2010-11-12 5 views
2

La gamme pour x et y est de 0 à 99.Générer 4000 coordonnées cartésiennes pseudo-aléatoires uniques FASTER?

Je suis actuellement en train de faire comme ceci:

excludeFromTrainingSet = [] 
while len(excludeFromTrainingSet) < 4000: 
    tempX = random.randint(0, 99) 
    tempY = random.randint(0, 99) 
    if [tempX, tempY] not in excludeFromTrainingSet: 
     excludeFromTrainingSet.append([tempX, tempY]) 

mais il faut les âges et je dois vraiment accélérer cela.

Des idées?

+1

je pourrais manquer quelque chose, mais ne sont pas là seulement 10000 coordonnées entières uniques * x-y * pour * x * et * y * entre 0 et 99? – fideli

+0

Comment est-ce aléatoire si vous excluez ce que vous avez déjà mis là-bas. Et pourquoi utilisez-vous une liste au lieu d'un tuple? – Falmarri

+0

@fideli Lol. C'est un bon point. Quoi qu'il en soit, je pourrais avoir besoin de faire cela pour de plus grandes plages donc la question est toujours pertinente. –

Répondre

6

Vincent Savard a un answer presque deux fois plus rapide que la première solution proposée ici.


Voici mon point de vue. Il faut tuples au lieu des listes pour hashability:

def method2(size): 
    ret = set() 
    while len(ret) < size: 
     ret.add((random.randint(0, 99), random.randint(0, 99))) 
    return ret 

Assurez-vous que la limite est sain d'esprit que d'autres answerers ont souligné. Pour une entrée sensée, c'est mieux algorithmiquement O (n) que O (n^2) à cause de l'ensemble au lieu de la liste. En outre, python est beaucoup plus efficace pour charger les locales que les globals, donc mettez toujours ce truc dans une fonction.

EDIT: En fait, je ne suis pas sûr qu'ils soient O (n) et O (n^2) respectivement à cause de la composante probabiliste mais les estimations sont correctes si n est pris comme le nombre d'éléments uniques ils voient. Ils seront tous les deux plus lents à mesure qu'ils s'approcheront du nombre total d'espaces disponibles. Si vous voulez un montant de points qui se rapproche du nombre total disponible, alors vous pourriez être mieux d'utiliser:

import random 
import itertools 

def method2(size, min_, max_): 
    range_ = range(min_, max_) 
    points = itertools.product(range_, range_) 
    return random.sample(list(points), size) 

Ce sera un porc de mémoire mais il est sûr d'être plus rapide que la densité des augmentations de points car elle évite regardant le même point plus d'une fois.Une autre option de profilage vaut (probablement mieux que dernier) serait

def method3(size, min_, max_): 
    range_ = range(min_, max_) 
    points = list(itertools.product(range_, range_)) 

    N = (max_ - min_)**2 
    L = N - size 
    i = 1 
    while i <= L: 
     del points[random.randint(0, N - i)] 
     i += 1 
    return points 
+0

+1 Ceci est une très bonne réponse. – hughdbrown

1

Vous pouvez faire une table de recherche de valeurs aléatoires ... faire un index au hasard dans cette table de consultation, puis à travers l'étape avec un compteur d'incrément statique ...

4

Je suis sûr que quelqu'un va venez ici avec une utilisation de numpy, mais que diriez-vous d'utiliser un ensemble et un tuple? .: par exemple

excludeFromTrainingSet = set() 
while len(excludeFromTrainingSet) < 40000: 
    temp = (random.randint(0, 99), random.randint(0, 99)) 
    if temp not in excludeFromTrainingSet: 
     excludeFromTrainingSet.add(temp) 

EDIT: Est-ce une boucle infinie car il n'y a que 100^2 = 10000 résultats possibles, et vous attendez jusqu'à ce que vous obtenez 40000?

+0

Belle prise! :-) –

+1

les ensembles n'ont pas de méthode append. – aaronasterling

1

Générer nombres prendra inévitablement un certain temps. Mais vous effectuez une recherche linéaire O (n) sur excludeFromTrainingSet, ce qui prend beaucoup de temps, surtout plus tard dans le processus. Utilisez un ensemble à la place. Vous pouvez également envisager de générer un certain nombre de jeux de coordonnées, par ex. pendant la nuit et les pickle, de sorte que vous n'avez pas à générer de nouvelles données pour chaque test (ne sais pas ce que vous faites, donc cela pourrait ou ne peut pas aider). L'utilisation de tuples, comme quelqu'un l'a noté, n'est pas seulement un choix sémantiquement correct, cela peut aussi aider avec les performances (la création de tuple est plus rapide que la création de liste). Edit: idiot me, en utilisant tuples est requis lors de l'utilisation des ensembles, car les membres de l'ensemble doivent être lavable et les listes sont indisponibles.

Mais dans votre cas, votre boucle ne se termine pas parce que 0..99 est 100 nombres et deux-tuples d'entre eux ont seulement 100^2 = 10000 combinaisons uniques. Corrigez cela, puis appliquez ce qui précède.

4

Ma suggestion:

def method2(size): 
    randints = range(0, 100) 
    excludeFromTrainingSet = set() 

    while len(excludeFromTrainingSet) < size: 
     excludeFromTrainingSet.add((random.choice(randints), random.choice(randints))) 
    return excludeFromTrainingSet 

au lieu de la génération 2 nombres aléatoires à chaque fois, vous devez d'abord générer la liste des numéros de 0 à 99 , puis vous choisissez 2 et ajoute à la liste. Comme d'autres l'ont souligné, il n'y a que 10 000 possibilités, donc vous ne pouvez pas boucler jusqu'à ce que vous obteniez 40 000, mais vous avez compris.

+0

C'est en fait plus lent. le code pour 'random.choice' est juste' return seq [int (self.random() * len (seq))] '' non seulement il fait toujours un appel au générateur de nombres aléatoires à chaque fois, mais il prend aussi la longueur de la liste. – aaronasterling

+0

De mes tests, c'est en fait plus rapide que le tien. Pourquoi? (Véritable question, je ne suis pas un expert Python.) Pour mes tests, j'ai évidemment remplacé la boucle for par une boucle while identique à la vôtre, et j'ai mis le code dans une fonction. –

+0

parce que votre algorithme est faux, maintenant que je le regarde. Vous testez seulement 10 0000 paires aléatoires et il n'y a aucune garantie qu'elles seront toutes uniques. Donc, vous pourriez très bien retourner moins que le montant désiré. Mine teste autant de paires que nécessaire pour obtenir la quantité désirée. – aaronasterling

4

Faites une liste de toutes les valeurs possibles (x, y):

allpairs = list((x,y) for x in xrange(99) for y in xrange(99)) 

# or with Py2.6 or later: 
from itertools import product 
allpairs = list(product(xrange(99),xrange(99))) 

# or even taking DRY to the extreme 
allpairs = list(product(*[xrange(99)]*2)) 

Lecture aléatoire la liste:

from random import shuffle 
shuffle(allpairs) 

Lire le premier 'n' valeurs:

n = 4000 
trainingset = allpairs[:n] 

Cela fonctionne assez bien sur mon ordinateur portable.

+0

+1 Une autre bonne solution. – hughdbrown

+0

Beaucoup mieux que la solution acceptée, car il n'y a que 10000 à choisir. C'est semblable à ce que j'allais taper. Je vais ajouter ma solution avec random.sample à la place (qui est seulement un peu plus rapide). –

+0

@Justin Peel. Encore une fois, comment dites-vous que c'est mieux quand votre solution tourne "légèrement" plus vite que celle-ci et que la mienne fonctionne beaucoup plus vite que cela. – aaronasterling

0

Prendre le code de Vince Savard:

>>> from random import choice 
>>> def method2(size): 
...  randints = range(0, 100) 
...  excludeFromTrainingSet = set() 
...  while True: 
...   x = size - len(excludeFromTrainingSet) 
...   if not x: 
...    break 
...   else: 
...    excludeFromTrainingSet.add((choice(randints), choice(randints)) for _ in range(x)) 
...  return excludeFromTrainingSet 
... 
>>> s = method2(4000) 
>>> len(s) 
4000 

Ce n'est pas un algorithme car il doit faire face à des collisions, mais tuple génération rend tolérable. Cela fonctionne dans environ une seconde sur mon ordinateur portable.

+0

En Python 2, vous pouvez obtenir un léger boost de vitesse avec 'while 1' au lieu de' True'. 'True' est traité comme un global dans Python 2 et comme une constante dans Python 3. – aaronasterling

0
## for py 3.0+ 
## generate 4000 points in 2D 
## 
import random 
maxn = 10000 
goodguys = 0 
excluded = [0 for excl in range(0, maxn)] 
for ntimes in range(0, maxn): 
    alea = random.randint(0, maxn - 1) 
    excluded[alea] += 1 
    if(excluded[alea] > 1): continue 
    goodguys += 1 
    if goodguys > 4000: break 
    two_num = divmod(alea, 100) ## Unfold the 2 numbers 
    print(two_num) 
Questions connexes