web-dev-qa-db-fra.com

Peut Python générer un nombre aléatoire qui exclut un ensemble de nombres, sans utiliser de récursivité?

J'ai regardé par-dessus Python Docs (j'ai peut-être mal compris), mais je n'ai pas vu qu'il y avait un moyen de le faire (regardez ci-dessous) sans appeler une fonction récursive.
Ce que j'aimerais faire, c'est générer une valeur aléatoire qui exclut les valeurs du milieu.

En d'autres termes,
Imaginons que je voulais que X soit un nombre aléatoire qui ne soit pas dans
range(a - b, a + b)
Puis-je le faire lors de la première passe,
ou
1. Dois-je générer constamment un nombre,
2. Vérifiez si dans range(),
3. Laver rinser ?

Quant à savoir pourquoi je ne souhaite pas écrire une fonction récursive,
1. on a l'impression que je ne devrais pas avoir à
2. l'ensemble des nombres pour lesquels je fais cela pourrait en fait finir par être assez grand, et
... J'entends que les débordements de pile sont mauvais, et je suis peut-être trop prudent.

Je suis sûr qu'il existe une façon agréable, pythonique et non récursive de le faire.

31
user890167

Utilisez random.choice (). Dans cet exemple, a est votre borne inférieure, la plage entre b et c est ignorée et d est votre borne supérieure.

import random
numbers = range(a,b) + range(c,d)
r = random.choice(numbers)
29
Junuxx

Générez un nombre aléatoire et mappez-le sur vos plages de nombres souhaitées.

Si vous vouliez générer un entier entre 1-4 ou 7-10, à l'exclusion 5 et 6, Tu pourrais:

  1. Génère un entier aléatoire dans la plage 1-8
  2. Si le nombre aléatoire est supérieur à 4, ajoutez 2 au résultat.

La cartographie devient:

Random number:    1  2  3  4  5  6  7  8
Result:           1  2  3  4  7  8  9 10

En procédant ainsi, vous n'avez jamais besoin de "relancer". L'exemple ci-dessus concerne les nombres entiers, mais il peut également être appliqué aux flottants.

41
Li-aung Yip

Une solution possible serait de simplement déplacer les nombres aléatoires hors de cette plage. Par exemple.

def NormalWORange(a, b, sigma):
    r = random.normalvariate(a,sigma)
    if r < a:
        return r-b
    else:
        return r+b

Cela générerait une distribution normale avec un trou dans la plage (a-b, a + b).

Edit: Si vous voulez des entiers, vous aurez besoin d'un peu plus de travail. Si vous voulez des entiers qui sont dans la plage [c, a-b] ou [a + b, d] alors ce qui suit devrait faire l'affaire.

def RangeWORange(a, b, c, d):
    r = random.randrange(c,d-2*b) # 2*b because two intervals of length b to exclude
    if r >= a-b:
        return r+2*b
    else:
        return r
9
Ken

J'ai peut-être mal compris votre problème, mais vous pouvez l'implémenter sans récursivité

def Rand(exclude):
    r = None
    while r in exclude or r is None:
         r = random.randrange(1,10)
    return r

Rand([1,3,9])

cependant, vous parcourez toujours les résultats jusqu'à ce que vous en trouviez de nouveaux.

7
zmo

La solution la plus rapide serait la suivante (avec a et b définissant la zone d'exclusion et c et d l'ensemble des bonnes réponses incluant la zone d'exclusion):

offset = b - a
maximum = d - offset
result = random.randrange(c, maximum)
if result >= a:
    result += offset
4
Andrew Gorcester

La réponse de Li-aung Yip rend le problème de récursion théorique, mais je dois souligner qu'il est possible de faire n'importe quel degré de récursivité sans se soucier de la pile. Cela s'appelle la "récursivité de la queue". Python ne prend pas directement en charge la récursivité de queue, car GvR pense que ce n'est pas cool:

http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html

Mais vous pouvez contourner cela:

http://paulbutler.org/archives/tail-recursion-in-python/

Je trouve intéressant que le bâton pense que la récursion "se sente mal". Dans les langages extrêmement orientés fonction, comme Scheme, la récursivité est inévitable. Il vous permet de faire une itération sans créer de variables d'état, ce que le paradigme de programmation fonctionnelle évite rigoureusement.

http://www.pling.org.uk/cs/pop.html

0

Vous avez toujours besoin de certains plage, c'est-à-dire une valeur min-max possible excluant vos valeurs intermédiaires.

Pourquoi ne choisissez-vous pas au hasard la "moitié" de la plage souhaitée, puis choisissez un nombre aléatoire dans cette plage? Par exemple.:

def Rand_not_in_range(a,b):
    rangechoices = ((0,a-b-1),(a+b+1, 10000000))
    # Pick a half
    fromrange = random.choice(rangechoices)
    # return int from that range
    return random.randint(*fromrange)
0
Francis Avila