As dict() is sorted according to internal hashes used for fast access and not by order in which you added elements to it, you can take it for be random enough and use dict.popitem().
But popitem() will also remove this element from the dictionary. So you may wish to use:
d = {...}
keys = d.keys()
item = keys.pop(0)
value = d[item]
instead. However, note that any dict with same/similar keys may have the same order of keys.
If you want proper random getting then do:
import random
d = {"red": "#ff0000", "green": "#00ff00", "blue": "#0000ff", "black": "#000000", "white": "#ffffff"}
keys = d.keys()
item = random.choice(keys)
value = d[item]
Of course, if you wish to prevent repetition you will have to use additional dict() and while loop:
import random
d = {"red": "#ff0000", "green": "#00ff00", "blue": "#0000ff", "black": "#000000", "white": "#ffffff"}
keys = d.keys()
used = {}
def get_rand_item (d):
item = random.choice(keys)
while item in used:
item = random.choice(keys)
value = d[item]
used[item] = None
return item, value
get_rand_item(d)
Here I use dict as storage because its search is faster than a list.
You asked for the fastest way. :D
While I am at it, let see if I can get even faster way of getting random items without repetitions:
from random import choice
class RandomGetter:
def __init__ (self, d, adapt=1):
self.keys = keys = d.keys()
if adapt:
# Could be done in place too
dct = {}
for k in keys:
dct[k] = (d[k], 0)
self.dct = dct
self.count = 1
else:
self.dct = d
# Assume all items have been visited
self.count = d[keys[0]][1]+1
self.visited = 0
self.length = len(self.dct)
def __len__ (self):
return self.length
def randitem (self, default=None):
if self.visited==self.length:
# After 'default' is returned (all items gotten),
# same RandomGetter() can be used again:
self.count += 1
self.visited = 0
return default
d = self.dct
kz = self.keys
c = self.count
key = choice(kz)
value, flag = d[key]
while flag>=c:
key = choice(kz)
value, flag = d[key]
d[key] = (value, flag+1)
self.visited += 1
return key, value
def next (self):
i = self.randitem()
if i==None: raise StopIteration
return i
def __iter__ (self):
while 1: yield self.next()
# Now testing:
# Lets create a dictionary of one million items:
d = dict.fromkeys(xrange(1000000))
# This takes about 0.128 seconds
# Now, lets initialize Rg
r = RandomGetter(d)
# If dict is not prepared in advance, as this one isn't we use adapt=1 and it takes
# about 8.92 seconds. Yack!
# Now measure time for each random getting:
from time import time
def check ():
randitem = r.randitem # Faster access to the method
e = []
for _ in xrange(len(r)):
t = time()
randitem()
e.append(time()-t)
print "Total/min/max/med/avg/(0 time count)"
e.sort()
s = sum(e)
if len(r)%2==0: m = (e[len(r)/2]+e[len(r)/2+1])/2
else: m = e[len(r)/2+1]
print s, e[0], e[-1], m, ("%.15f" % (s/1000000)), e.count(0.0)
check()
# It yields following results on my machine:
# About 25.224 seconds to randomly get all 1000000 items
# Minimal time needed is not measurable using this technique so it is 0.0
# Maximal time needed to get an item is about 1.678 seconds
# Median results with 0.0, thus we know that more than half randomly gotten items took practically no time
# In fact, there are about 998808 items with getting time of 0.0 seconds
# Average getting time is about 0.000025224 seconds
# By examining results closely I deduced that only last few items took a long time to get them.
# All in all, not bad for one million items, in my opinion anyway.
# For dict of 2000 items, total time was 0.016 and that was also the maximal value and it was for the last gotten item
# Time didn't cross one second until length of a given dictionary wasn't bigger than 100000
# If you want, you can run my code through timeit to recheck, but it seems that it is faster
# than suggested random dictionary.