mal00
mal00

Reputation: 43

how to make sure that two numbers next to each other in a list are different

I have a simple code that generates a list of random numbers.

x = [random.randrange(0,11) for i in range(10)]

The problem I'm having is that, since it's random, it sometimes produces duplicate numbers right next to each other. How do I change the code so that it never happens? I'm looking for something like this:

[1, 7, 2, 8, 7, 2, 8, 2, 6, 5]

So that every time I run the code, all the numbers that are next to each other are different.

Upvotes: 1

Views: 768

Answers (7)

Ivan Reshetnikov
Ivan Reshetnikov

Reputation: 410

def random_sequence_without_same_neighbours(n, min, max):
    x = [random.randrange(min, max + 1)]
    uniq_value_count = max - min + 1
    next_choises_count = uniq_value_count - 1
    for i in range(n - 1):
        circular_shift = random.randrange(0, next_choises_count)
        x.append(min + (x[-1] + circular_shift + 1) % uniq_value_count)
    return x

random_sequence_without_same_neighbours(n=10, min=0, max=10)

Upvotes: 0

Michael Szczesny
Michael Szczesny

Reputation: 5026

O(n) solution by adding to the last element randomly from [1,stop) modulo stop

import random

x = [random.randrange(0,11)]
x.extend((x[-1]+random.randrange(1,11)) % 11 for i in range(9))

x

Output

[0, 10, 4, 5, 10, 1, 4, 8, 0, 9]

Upvotes: 2

Samwise
Samwise

Reputation: 71517

Here's an approach that doesn't rely on retrying:

>>> import random
>>> x = [random.choice(range(12))]
>>> for _ in range(9):
...     x.append(random.choice([*range(x[-1]), *range(x[-1]+1, 12)]))
...
>>> x
[6, 2, 5, 8, 1, 8, 0, 4, 6, 0]

The idea is to choose each new number by picking from a list that excludes the previously picked number.

Note that having to re-generate a new list to pick from each time keeps this from actually being an efficiency improvement. If you were generating a very long list from a relatively short range, though, it might be worthwhile to generate different pools of numbers up front so that you could then select from the appropriate one in constant time:

>>> pool = [[*range(i), *range(i+1, 3)] for i in range(3)]
>>> x = [random.choice(random.choice(pool))]
>>> for _ in range(10000):
...     x.append(random.choice(pool[x[-1]]))
...
>>> x
[0, 2, 0, 2, 0, 2, 1, 0, 1, 2, 0, 1, 2, 1, 0, ...]

Upvotes: 3

emanuele-f
emanuele-f

Reputation: 394

x = []

while len(x) < 10:
    r = random.randrange(0,11)

    if not x or x[-1] != r:
        x.append(r)

x[-1] contains the last inserted element, which we check not to be the same as the new random number. With not x we check that the array is not empty, as it would generate a IndexError during the first iteration of the loop

Upvotes: 6

will-hedges
will-hedges

Reputation: 1284

Function solution that doesn't iterate to check for repeats, just checks each add against the last number in the list:

import random

def get_random_list_without_neighbors(lower_limit, upper_limit, length):
    res = []
    # add the first number
    res.append(random.randrange(lower_limit, upper_limit))
    while len(res) < length:
        x = random.randrange(lower_limit, upper_limit)
        # check that the new number x doesn't match the last number in the list
        if x != res[-1]:
            res.append(x)

    return res
>>> print(get_random_list_without_neighbors(0, 11, 10)
[10, 1, 2, 3, 1, 8, 6, 5, 6, 2]

Upvotes: 0

Tau n Ro
Tau n Ro

Reputation: 108

It's not to much pythonic but you can do something like this

import random

def random_numbers_generator(n):
    "Generate a list of random numbers but without two duplicate numbers in a row "
    result = []
    for _ in range(n):
        number = random.randint(1, n)
        if result and number == result[-1]:
            continue
        result.append(number)
    return result


print(random_numbers_generator(10))

Result:

3, 6, 2, 4, 2, 6, 2, 1, 4, 7]

Upvotes: -1

Jon Clements
Jon Clements

Reputation: 142206

from random import randrange
from itertools import islice, groupby

# Make an infinite amount of randrange's results available
pool = iter(lambda: randrange(0, 11), None)
# Use groupby to squash consecutive values into one and islice to at most 10 in total
result = [v for v, _ in islice(groupby(pool), 10)]

Upvotes: 0

Related Questions