Comment générer un nombre aléatoire de 4 chiffres ne commençant pas par 0 et ayant des chiffres uniques?

cela fonctionne presque bien, mais le nombre commence par 0 parfois:

import random
numbers = random.sample(range(10), 4)
print(''.join(map(str, numbers)))

j'ai trouvé beaucoup d'exemples mais aucun ne garantit que la séquence ne commencera pas par 0 .

37
demandé sur Salman A 2017-04-24 19:25:12
la source

12 ответов

nous produisons le premier chiffre dans la gamme 1 - 9, puis prenons les 3 suivants des autres chiffres:

import random

# We create a set of digits: {0, 1, .... 9}
digits = set(range(10))
# We generate a random integer, 1 <= first <= 9
first = random.randint(1, 9)
# We remove it from our set, then take a sample of
# 3 distinct elements from the remaining values
last_3 = random.sample(digits - {first}, 3)
print(str(first) + ''.join(map(str, last_3)))

Les nombres générés sont équiprobable, et nous obtenons un nombre valide en une seule étape.

64
répondu Thierry Lathuille 2017-04-24 19:56:46
la source

juste boucle jusqu'à ce que vous avez quelque chose que vous aimez:

import random

numbers = [0]
while numbers[0] == 0:
    numbers = random.sample(range(10), 4)

print(''.join(map(str, numbers)))
30
répondu Austin Hastings 2017-04-24 19:36:06
la source

c'est très similaire aux autres réponses mais au lieu de sample ou shuffle vous pourriez dessiner un entier aléatoire dans la gamme 1000-9999 jusqu'à ce que vous obtenez un qui contient seulement des chiffres uniques:

import random

val = 0  # initial value - so the while loop is entered.
while len(set(str(val))) != 4:  # check if it's duplicate free
    val = random.randint(1000, 9999)

print(val)

comme @Claudio l'a souligné dans les commentaires, la fourchette ne doit en fait être 1023 - 9876 parce que les valeurs en dehors de cette fourchette contiennent des chiffres en double.

en général random.randint sera beaucoup plus rapide que random.shuffle ou random.choice donc même s'il est plus probable qu'il faille dessiner plusieurs fois (comme le souligne @karakfa) c'est jusqu'à 3 fois plus rapide que n'importe quelle shuffle , choice approche qui a aussi besoin de join les chiffres simples.

20
répondu MSeifert 2017-04-25 23:09:56
la source

Je ne connais pas bien Python, mais quelque chose comme

digits=[1,2,3,4,5,6,7,8,9] <- no zero
random.shuffle(digits)
first=digits[0] <- first digit, obviously will not be zero
digits[0]=0 <- used digit can not occur again, zero can
random.shuffle(digits)
lastthree=digits[0:3] <- last three digits, no repeats, can contain zero, thanks @Dubu

une itération plus utile, créant en fait un nombre:

digits=[1,2,3,4,5,6,7,8,9]   # no zero
random.shuffle(digits)
val=digits[0]                # value so far, not zero for sure
digits[0]=0                  # used digit can not occur again, zero becomes a valid pick
random.shuffle(digits)
for i in range(0,3):
  val=val*10+digits[i]       # update value with further digits
print(val)

après avoir volé des pièces d'autres solutions, plus l'application de la pointe de @DavidHammen:

val=random.randint(1,9)
digits=[1,2,3,4,5,6,7,8,9]
digits[val-1]=0
for i in random.sample(digits,3):
  val=val*10+i
print(val)
15
répondu tevemadar 2017-04-26 14:38:08
la source

[fixe] Shift les quatre chiffres sur une position n'est pas juste. Swap menant zéro avec position fixe n'est pas non plus juste. Mais l'échange aléatoire du zéro de tête avec l'une des neuf positions est correcte et donne la probabilité égale:

""" Solution: randomly shuffle all numbers. If 0 is on the 0th position,
              randomly swap it with any of nine positions in the list.

  Proof
    Lets count probability for 0 to be in position 7. It is equal to probability 1/10 
  after shuffle, plus probability to be randomly swapped in the 7th position if
  0 come to be on the 0th position: (1/10 * 1/9). In total: (1/10 + 1/10 * 1/9).
    Lets count probability for 3 to be in position 7. It is equal to probability 1/10
  after shuffle, minus probability to be randomly swapped in the 0th position (1/9)
  if 0 come to be on the 0th position (1/10) and if 3 come to be on the 7th position
  when 0 is on the 0th position (1/9). In total: (1/10 - 1/9 * 1/10 * 1/9).
    Total probability of all numbers [0-9] in position 7 is:
  9 * (1/10 - 1/9 * 1/10 * 1/9) + (1/10 + 1/10 * 1/9) = 1
    Continue to prove in the same way that total probability is equal to
  1 for all other positions.
    End of proof. """

import random
l = [0,1,2,3,4,5,6,7,8,9]
random.shuffle(l)
if l[0] == 0:
    pos = random.choice(range(1, len(l)))
    l[0], l[pos] = l[pos], l[0]
print(''.join(map(str, l[0:4])))
11
répondu foo bar 2017-04-25 14:36:18
la source

méthode d'échantillonnage de rejet. Créer une combinaison aléatoire de 4 chiffres à partir de 10 chiffres et rééchantillonner si elle ne correspond pas aux critères.

r4=0    
while r4 < 1000:
    r4=int(''.join(map(str,random.sample(range(10),4))))

a remarqué que c'est essentiellement la même chose que la réponse de @Austin Hasking 151950920

11
répondu karakfa 2017-05-23 14:47:19
la source

vous pouvez utiliser la gamme complète pour 3 numéros, puis choisir le numéro de tête parmi les autres numéros:

import random
numbers = random.sample(range(0,10), 3)
first_number = random.choice(list(set(range(1,10))-set(numbers)))
print(''.join(map(str, [first_number]+numbers)))

une Autre façon si le choix doit être répété (et si vous restez raisonnable sur le nombre de chiffres), est de calculer la liste des sorties possibles à l'aide de itertools.permutations , en filtrant ceux avec un zéro, et la construction d'une liste d'entiers:

import itertools,random

l = [int(''.join(map(str,x))) for x in itertools.permutations(range(10),4) if x[0]]

c'est un peu de temps de calcul, mais après que vous pouvez appeler:

random.choice(l)

autant de fois que tu veux. Il est très rapide et fournit un aléatoire uniformément distribué.

6
répondu Jean-François Fabre 2017-04-25 21:16:35
la source

Je ne connais pas Python donc je vais poster une solution pseudo-code-ish pour ce problème spécifique:

  • créez une variable de recherche contenant une liste de chiffres basée sur 0:

    lu = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
  • générer quatre nombres aléatoires basés sur 0 comme suit:

    r1 = random number between 0 and 8
    r2 = random number between 0 and 8
    r3 = random number between 0 and 7
    r4 = random number between 0 and 6
    
  • utilisez la variable de recherche pour convertir les nombres aléatoires en chiffres un par un. Après chaque recherche, modifier la variable de recherche en supprimant le chiffre qui a été utilisé:

    d1 = lu[r1]
    lu.remove(d1)
    lu.insert(0)
    
    d2 = lu[r2]
    lu.remove(d2)
    
    d3 = lu[r3]
    lu.remove(d3)
    
    d4 = lu[r4]
    lu.remove(d4)
    
  • imprimer le résultat:

    print concatenate(d1, d2, d3, d4)
    

il est possible de généraliser un peu cette idée. Par exemple, vous pouvez créer une fonction qui accepte une liste (des chiffres) et un nombre (longueur désirée de résultat); la fonction retourne le nombre et muter la liste en supprimant utilisé chiffres. Ci-dessous un JavaScript mise en œuvre de cette solution:

function randomCombination(list, length) {
    var i, rand, result = "";
    for (i = 0; i < length; i++) {
        rand = Math.floor(Math.random() * list.length);
        result += list[rand];
        list.splice(rand, 1);
    }
    return result;
}

function desiredNumber() {
    var list = [1, 2, 3, 4, 5, 6, 7, 8, 9],
        result;
    result = randomCombination(list, 1);
    list.push(0);
    result += randomCombination(list, 3);
    return result;
}

var i;
for (i = 0; i < 10; i++) {
    console.log(desiredNumber());
}
4
répondu Salman A 2017-04-25 23:42:53
la source

Voici comment je le ferais

while True:
    n = random.randrange(1000, 10000)
    if len(set(str(n))) == 4: # unique digits
        return n

de façon plus générale, étant donné qu'il s'agit d'une génératrice, vous pouvez utiliser les éléments encastrés" 151970920 filter et next pour prendre le premier élément qui satisfait à une certaine fonction d'essai.

numbers = iter(lambda: random.randrange(1000, 10000), None) # infinite generator
test = lambda n: len(set(str(n))) == 4
return next(filter(test, numbers))
2
répondu Colonel Panic 2017-04-25 18:09:24
la source

combinent generators avec next

une façon pythonique d'écrire serait d'utiliser 2 générateurs imbriqués et next :

from random import randint
from itertools import count

print(next(i for i in (randint(1023, 9876) for _ in count()) if len(set(str(i))) == 4))
# 8756

il s'agit essentiellement d'une variante à une seule doublure de la réponse de @MSeifert

Préprocéder à tous les nombres acceptables

si vous avez besoin de beaucoup de nombres aléatoires, vous pourriez investir du temps et de la mémoire pour prétraitement tous les nombres acceptables:

import random    
possible_numbers = [i for i in range(1023, 9877) if len(set(str(i))) == 4]

1023 et 9877 sont utilisés comme des limites parce que aucun int inférieur à 1023 ou supérieure à 9876 peut avoir les 4 uniques, distince numéros.

alors, vous avez juste besoin de random.choice pour une génération très rapide:

print(random.choice(possible_numbers))
# 7234
1
répondu Eric Duminil 2017-05-23 15:10:27
la source

clause de non-responsabilité: c'est une terrible approche anti-Python, strictement pour le benchmarking (voir les commentaires de @DavidHammen autour de, et http://ideone.com/qyopLF ) L'idée est de générer les numéros de séquence des chiffres en une seule étape, puis fixer toutes les collisions:

rnd=random.randint(0,4535)
(rnd,d1)=divmod(rnd,9)
(rnd,d2)=divmod(rnd,9)
#(rnd,d3)=divmod(rnd,8)
#(rnd,d4)=divmod(rnd,7)
(d4,d3)=divmod(rnd,8) # miracle found: 1 divmod happens to run faster than 2

maintenant nous avons d1 = 0..8, d2=0..8, d3=0..7, d4=0..6, il peut être testé via l'exécution de l'extrait avec rnd=4535 (4535=9*9*8*7-1, par la voie)

Tout d'abord, d1 doit être corrigé

d1=d1+1 # now d1 = 1..9

puis d2 doit" sauter "d1 si nécessaire

if d2>=d1
  d2=d2+1 # now d2 = 0..9 "-" d1

alors la même chose doit être faite avec les chiffres restants, devenant laid rapidement:

if d3>=d1:
  d3=d3+1    # now d3 = 0..8 "-" d1
  if d3>=d2:
    d3=d3+1  # now d3 = 0..9 "-" {d1,d2}
elif d3>=d2: # this branch prepares for the other variant
  d3=d3+1
  if d3>=d1: # ">=" is preserved for consistency, here "==" may occur only
    d3=d3+1

et la dernière partie est la catastrophique:

if d4>=d1:
  d4=d4+1
  if d4>=d2:
    d4=d4+1
    if d4>=d3:
      d4=d4+1
  elif d4>=d3:
    d4=d4+1
    if d4>=d2:
      d4=d4+1
elif d4>=d2:
  d4=d4+1
  if d4>=d1:
    d4=d4+1
    if d4>=d3:
      d4=d4+1
  elif d4>=d3:
    d4=d4+1
    if d4>=d1:
      d4=d4+1
elif d4>=d3:
  d4=d4+1
  if d4>=d2:
    d4=d4+1
    if d4>=d1:
      d4=d4+1
  elif d4>=d1:
    d4=d4+1
    if d4>=d2:
      d4=d4+1

pour des nombres plus longs, il pourrait fonctionner plus rapidement avec des bitfields, mais je ne vois pas une façon triviale. (Vérifier les >= relations une fois n'est pas suffisant, parce que la collision peut facilement se produire après avoir fait une incrémentation. par exemple, d1=1, d2=2, d3=1: d3 entre en collision avec d1, mais il n'entre pas en collision avec D2 initialement. Cependant après "puching The hole" à 1, d3 devient 2 et maintenant il entre en collision avec d2. Il n'y a aucun moyen futile de repérer cette collision à l'avance)

Comme code pue comme l'enfer, j'ai mis une étape de vérification à la fin

val = d1*1000 + d2*100 + d3*10 + d4
#if len(set(str(val))) != 4: print(str(val)+" "+str(o1)+","+str(o2)+","+str(o3)+","+str(o4))
if len(set(str(val))) != 4: print(val)

il est déjà plus rapide que l'autre code vraiment rapide (le la vérification commentée affiche les chiffres originaux conservés après le divmod-s, à des fins de débogage. Ce n'est pas le genre de code qui fonctionne immédiatement...). Commenter les deux vérifications le rend encore plus rapide.

EDIT: à propos de vérifier ceci et cela

C'est une façon de maintenir un 1:1 rapport entre le nombre minimal d'entrées valides (0...4535) et les sorties valides (les 9*9*8*7 possibilité de numéros à 4 chiffres avec des chiffres distincts, pas de départ avec-0). Ainsi une boucle simple peut et doit générer tous les nombres, ils peuvent être vérifiés un par un et ils peuvent être recueillis dans un ensemble par exemple afin de voir s'ils sont tous des résultats distincts

pratiquement:

collect=set()
for rnd in range(0,4536):
    (rnd,d1)=divmod(rnd,9)
    ... rest of the code, also the verification step kept active ...
    collect.add(val)
print(len(collect))

1) Il n'imprimera rien dans la boucle (tous les résultats sont des nombres à 4 chiffres avec des chiffres distincts)

2) il affichera 4536 à la fin (tous les résultats sont distincts)

on peut ajouter une vérification pour le premier chiffre (d1), ici et maintenant je suppose que

" (quelque chose de mod 9)+1 " ne sera pas 0.

0
répondu tevemadar 2017-05-09 16:24:12
la source

permet des zéros après le premier chiffre -

numbers = random.sample(range(1,10),1) + random.sample(range(10),3)
-6
répondu buckettt 2017-04-24 19:31:36
la source

Autres questions sur