0

Consider the following code:

for i in range(size-1):
    for j in range(i+1,size):
        print((i,j))

I need to go through this for-loop in a random fashion. I attempt to write a generator to do such a thing

def Neighborhood(size):
    for i in shuffle(range(size-1)):
        for j in shuffle(range(i+1), size):
            yield i, j
for i,j in Neighborhood(size):
    print((i,j))

However, shuffle cannot be applied to whatever object range is. I do not know how to remedy the situation, and any help is much appreciated. I would prefer a solution avoid converting range to a list, since I need speed. For example, size could be on the order of 30,000 and i will do perform this for loop around 30,000 times.

I also plan to escape the for loop early, so I want to avoid solutions that incorporate shuffle(list(range(size)))

3 Answers3

2

You can use random.sample.

The advantage of using random.sample over random.shuffle, is , it can work on iterators, so in :

  • Python 3.X you don't need to convert range() to list
  • In Python 2,X, you can use xrange
  • Same Code can work in Python 2.X and 3.X

Sample code :

n=10
l1=range(n)
for i in sample(l1,len(l1)):
    l2=range(i,n)
    for j in sample(l2,len(l2)):
        print(i,j)

Edit :

As to why I put in this edit, go through the comments.

def Neighborhood(size):
    range1 = range(size-1)
    for i in sample(range1, len(range1)):
        range2 = range(i+1)
        for j in sample(range2, len(range2)):
            yield i, j
Kaushik NP
  • 6,733
  • 9
  • 31
  • 60
1

A simple way to go really random, not row-by-row:

def Neighborhood(size):
    yielded = set()
    while True:
        i = random.randrange(size)
        j = random.randrange(size)
        if i < j and (i, j) not in yielded:
            yield i, j
            yielded.add((i, j))

Demo:

for i, j in Neighborhood(30000):
    print(i, j)

Prints something like:

2045 5990
224 5588
1577 16076
11498 15640
15219 28006
8066 10142
7856 8248
17830 26616
...

Note: I assume you're indeed going to "escape the for loop early". Then this won't have problems with slowing down due to pairs being produced repeatedly.

Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107
  • This is definitely an approach I will try to implement along with the other suggestions in the thread, especially in tandem with simulated annealing. Can I modify the code so that it runs as a generator, that way I don't create the entire set ahead of time, or does this code already due this. Sorry I'm new to generators and the yield command. – Jonathan Davidson Sep 21 '17 at 04:23
  • @JonathanDavidson It already is a generator. Btw, you said you "want to avoid solutions that incorporate shuffle(list(range(size)))" but then you accepted one that practically does exactly that... – Stefan Pochmann Sep 21 '17 at 14:16
  • I thought sampling an iterator does not do that. My mistake. I have already tried both yours and the other solution, in different versions of my algorithm and I am testing to see which approach works better. I appreciate your contribution. – Jonathan Davidson Sep 21 '17 at 19:51
-1

I don't think you can randomly traverse an Iterator. You can predefine the shuffled lists, though

random iteration in Python

L1 = list(range(size-1))
random.shuffle(L1) 
for i in L1:
    L2 = list(range(i+1, size))
    random.shuffle(L2) 
    for j in L2:
        print((i,j))

Of course, not optimal for large lists

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245