1

I have a list of integers: l = [1,2,3,4]

For each element in this list I need to randomly select a different element and perform some operation on it:

for i in range(len(l)):
   idx = # a random index which is NOT equal to i
   # do something with the element at idx

I'm still pretty new to Python and can't determine if there is a way to do this without resorting to a loop where I generate a random index and only stop the loop if the random number is not equal to i. Any suggestions would be greatly appreciated.

RobertJoseph
  • 7,968
  • 12
  • 68
  • 113
  • You will find help here: https://stackoverflow.com/questions/25200220/generate-a-random-derangement-of-a-list – WNG Jul 14 '17 at 15:03

5 Answers5

4

How about this: Generate a random number between1 0 and N - 1 (N being the length of the list in this case), then add one to that number if it is equal to or greater than i.

for i in range(len(l)):
    idx = random.randrange(len(l) - 1)
    idx = idx + 1 if idx >= i else idx
    # do stuff with idx

This way, all the numbers rolled above i are shifted "one up":

       0      i          N
before *****************
after  ******* **********

Or, in a single line, you could generate a number between1 i + 1 and N + i and take that number modulo N, effectively wrapping it around after the end of the list:

    idx = random.randrange(i + 1, len(l) + i) % len(l)

       0      i          N
before         *****************
after  ******* **********

1) Here meaning including the lower bound and excluding the upper bound, using randrange

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Yes, that you @tobias_k that is exactly what I was looking for. I'll accept it as soon as SO will allow me. – RobertJoseph Jul 14 '17 at 15:11
  • why not just idx = idx + 1 if idx == i else idx? – Alexey Jul 14 '17 at 15:21
  • 1
    @Alexey You have to shift all the "higher" `idx`, otherwise the chance for `i+1` is twice as high, and `len(l)-1` will never be rolled. See my edit. You _could_ do `idx = len(l)-1 if idx == i else idx`, though. – tobias_k Jul 14 '17 at 15:26
1
l=[1,2,3,4,5]
import random as rd 
def remove_index(list,index):
    res=list[:]
    res.pop(index)
    return res

for i in range(len(l)):
    print rd.choice(remove_index(l,i))
Ohad Rubin
  • 460
  • 3
  • 13
0

Short numpy approach:

import numpy as np

l = np.array([1,2,3,4])
for i in range(len(l)):
    idx = random.choice(np.where(l != l[i])[0])
    # do stuff with idx
    print(i, idx)

The output (showing index difference):

0 1
1 2
2 3
3 1
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
  • This does not work properly if the list contained duplicate elements (and assuming that an equal element could be picked, as long as it's at a different position) – tobias_k Jul 14 '17 at 15:53
  • 1
    @tobias_k, nobody mentioned about *duplicate elements*, the OP wrote about different indices. Why are writing about *duplicate elements*? – RomanPerekhrest Jul 14 '17 at 15:59
  • 1
    Nobody mentioned duplicate elements, but that does not mean that they are not a concern. OP asked for a random index other than the current index, whereas your code produces a random index other than any index that has the current element or a duplicate thereof. – tobias_k Jul 14 '17 at 16:01
  • @tobias_k, if the OP confirmed that – RomanPerekhrest Jul 14 '17 at 16:05
0

Another approach:

size = len(l)
idxl = [random.choice([j for j in range(size) if j != i]) for i in range(size)]

for idx in idxl:
    # do something with the element at idx
-1
import random

l = [1, 2, 3, 4]

for x in l:
    new_el = random.choice(l)
    while new_el == x:
        new_el = random.choice(l)
    print new_el
Alexey
  • 1,366
  • 1
  • 13
  • 33
  • That is _exactly_ what I described in my question @Alexey. I'm looking for something _other_ than that. I was hoping NumPy, etc. would be able to do this. – RobertJoseph Jul 14 '17 at 15:09