How to limit the generation of pseudorandom numbers to nonzero and non-repeating numbers?

7

I wish the numbers did not repeat themselves and were nonzero.

import random


Sort1 = int(60 * random.random())   


Sort2 = int(60 * random.random())


Sort3 = int(60 * random.random())

Sort4 = int(60 * random.random())

Sort5 = int(60 * random.random())

Sort6 = int(60 * random.random())

lista = [Sort1,Sort2,Sort3,Sort4,Sort5,Sort6]

lista.sort()


print (Sort1,Sort2,Sort3,Sort4,Sort5,Sort6)


print (lista)
    
asked by anonymous 27.12.2016 / 23:32

4 answers

8

Use random.sample :

>>> random.sample(range(1,61), 6)
[39, 15, 37, 18, 52, 60]

Explanation:

A method that guarantees a fair draw (ie each element has the same chance of being drawn) and without repetition is the generation of a search space permutation - for example by Fisher-Yates algorithm - which removes only the first% of% elements you want.

If your search space is small, the implementation proposed by Luiz Vieira (create a list with all the elements and swapping it) is the simplest and perhaps most efficient way. In your question, the numbers go only from k to 1 , so this simple solution is the one I would use. If you do not have numpy access, you can also use 60 function:

>>> nums = list(range(1, 61))
>>> random.shuffle(nums)
>>> nums[:6]
[14, 12, 56, 26, 42, 10]

On the other hand, if the search space was too large (eg you want 10 numbers from 1 to 1 billion) this implementation would be impracticable - not so much by the time but by the memory spent on the creation of the list. In this case, an alternative implementation would be:

def random_elements(a_sortear, total):
    state = {}
    for i in range(a_sortear):
        # Troca o state[i] com um elemento aleatório
        swap_with = random.randint(i, total - 1)
        state[i], state[swap_with] = state.get(swap_with, swap_with), state.get(i, i)
    return [state[i]+1 for i in range(a_sortear)]

print (random_elements(10, 1000000000))

Font (adapted)

This is a partial application of the same algorithm:

  • Instead of the list being created explicitly, it is implied that the index element random.shuffle has the value i if it is absent from the set:

    state.get(swap_with, swap_with) # O valor padrão, se ausente, é o próprio índice
    
  • When two elements are changed (just like the original algorithm), they are explicitly placed in the array:

    state[i], state[swap_with] = state.get(swap_with, swap_with), state.get(i, i)
    
  • The algorithm to toggle when the desired number of elements has already been obtained:

    for i in range(a_sortear): # Não vai até total, mas para em a_sortear
        swap_with = random.randint(i, total - 1) # Sorteia de 0 a total-1
        ...
    
  • For the result to go from i to 1 , instead of N to 0 , it moves whole set 1 to the right:

    return [state[i]+1 for i in range(a_sortear)]
    
  • That is, in this implementation both the time and the memory spent are proportional to the number of elements one wants, instead of the total number of elements available to be drawn.

    Finally, there is the alternative algorithm in which you simply draw lots of numbers and, if any one comes back, the draw is done again until all the results are different. This method may be even more efficient than the partial Fisher-Yates application when the overall set is too large (since the chance of collision is small). The Answers from Icarus Dantas and do Miguel give examples of implementation.

    The use of N-1 - from what I understood from fonts - choose one or another implementation based on what would be most efficient depending on the sizes of the total set and the number of elements to be sorted.

        
    28.12.2016 / 11:53
    10

    If you want non-repeated numbers in a range ([1, 60], by your example), a very simple solution is to do a #.

    Using numpy is quite easy with numpy.random.permutation function: / p>

    import numpy as np
    nums = np.random.permutation(np.arange(1, 61))
    print(nums)
    

    This code works like this:

  • The call np.arange(1, 61) generates a list with numbers from 1 to 60 (because you said that the numbers should not be 0 - if you can have 0, simply pass the maximum number + 1 directly to the call of np.random.permutation a follow).
  • This list serves as input for the call np.random.permutation , which just takes that list and shuffles the positions.
  • Sample code execution result:

    [ 3 37 59 54 58 16  1 19 36 40 34 31 18 13 25 50 23  9 41 46 27  8 15 47 24
     29 57 43 56 22 11 48 26 39  7 17 55 21 20 42 30 35 32 14 12 28 33 53 38 60
      2 52 44 45  6 49 10  5 51  4]
    
        
    28.12.2016 / 01:13
    4

    Here is another alternative, using a set , this default implemented in the python language itself already avoids duplicates.

    In the example below n is the number of entries you want in your new list, I put 6 based on the code that you put in the question:

    from random import randint
    
    lista = set()
    n = 6
    
    while len(lista) < n:
        lista.add(randint(1, 60))
    
    lista = sorted(lista) # transformar em lista e ordenar
    print(lista) 
    

    DEMONSTRATION

    If you put a maximum random number less than n , ex: 5, you will be in infinite loop, to cover this hypothesis you can do:

    from random import randint
    
    lista = set()
    n = 6
    max_random = 60
    
    if(max_random > n):
        while len(lista) < n:
            lista.add(randint(1, max_random))
        print(sorted(lista)) # transformar em lista e ordenar
    else:
        print('O numero random não pode ser menor que n')
    
        
    28.12.2016 / 10:59
    2

    See if this solves your problem:

    from random import randint
    
    lista = []
    
    n = 40
    
    for i in range(n):
        num = randint(1, 60) # Intervalo de 1 a 60.
        while num == 0 or num in lista:
            num = randint(1, 60)
        lista.append(num)
    
    lista.sort()
    
    print lista
    
        
    28.12.2016 / 01:05