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

J'ai regardé Docs Python (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 je voudrais faire est de générer une valeur aléatoire qui exclut les valeurs au milieu.

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

Quant à la raison pour laquelle je ne souhaite pas écrire une fonction récursive,
1. il 'se sent comme' 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 pourrais juste être trop prudent en faisant cela.

Je suis sûr qu'il y a une belle façon pythonique et non récursive de le faire.

30
demandé sur pradyunsg 2012-05-19 19:46:56

7 réponses

Utilisez aléatoire.choix(). Dans cet exemple, a est votre limite inférieure, la plage entre b et c est ignorée et d Est votre limite supérieure.

import random
numbers = range(a,b) + range(c,d)
r = random.choice(numbers)
27
répondu Junuxx 2012-05-19 16:02:46

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

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

  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.

Le mappage devient:

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

Faisant de cette façon, vous n'avez jamais besoin de "re-roll". L'exemple ci-dessus est pour les entiers, mais il peut aussi être appliqué aux flotteurs.

41
répondu Li-aung Yip 2012-05-19 16:06:34

Une solution possible serait de simplement déplacer le 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

Qui 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 entiers dans l'intervalle [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
répondu Ken 2012-05-19 16:28:06

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 êtes toujours en boucle sur les résultats jusqu'à ce que vous en trouviez de nouveaux.

6
répondu zmo 2012-05-19 15:56:48

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 y compris la zone d'exclusion):

offset = b - a
maximum = d - offset
result = random.randrange(c, maximum)
if result >= a:
    result += offset
4
répondu Andrew Gorcester 2012-05-19 16:07:39

Vous avez toujours besoin de d'une plage , c'est-à-dire d'une valeur possible min-max excluant vos valeurs moyennes.

Pourquoi ne pas d'abord choisir au hasard quelle "moitié" de la plage que vous voulez, puis choisir 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
répondu Francis Avila 2012-05-19 16:05:29

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

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

Mais vous pouvez vous déplacer ceci:

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

Je trouve intéressant que stick pense que la récursivité "se sent mal". Dans les langages extrêmement axés sur les fonctions, tels que 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
répondu Isaac Rabinovitch 2012-09-24 18:11:06