Say I have a universal set of indexed objects, U, and a subset of these objects, S. S is large (say, 1,000,000 elements), however U is MUCH larger (say, 100,000,000 at least).
I would like to perform two basic operations on these sets:
(1) Given any integer x from 0 to the size of U minus 1, check for membership of S, if not a member, then add x to S, and
(2) Select (and remove) a random element from S.
In order to perform the first part of operation (1), it makes sense to me to keep a boolean vector v the size of U, where the value is true if element x is a member of set S.
However, because U is so much larger than S, choosing a random element in v and hoping that it is also an element in S does not make sense. I if U is 100 times larger than S, then it would only find an element of S, on average, one time every 100 tries.
So, in order to perform the second operation, it makes sense to maintain a list of indices of elements that are in S and select a random element from that.
The only problem now, is that there is now two copies of the same data, and they both need to be updated separately with each operation. Here is the pseudocode for the first operation:
** operation 1 - check membership and add **
input: boolean vector, v
integer vector, S
integer, x
if v[x] is not true:
v[x] = true
append x to S
return
That is relative simple, but it has to update the vector of indices even though it didn't use it. Here is the second operation:
** operation 2 - select and remove random element of S **
input: boolean vector, v
integer vector, S
generate random integer x between 0 and size of S
set v[S[x]] to false
remove S[x] from S
return
Maintaining two copies of the data has made both of those operations more complicated, because each has to update both data structures, even if it only needs one. Is this bad practice?
The only alternative I could come up with is to use one or the other. But that makes one operation simpler, but the other more complicated. For example (only the more complicated ones are given):
** operation 1 - check membership and add**
input: integer vector, S
integer, x
iterate over S
if x in S:
return
else:
append x to S
return
So every time, it will have to iterate over the entire S, instead of a single look-up, and
** operation 2 - select and remove random element of S **
input: boolean vector, v
while true:
generate random integer x between 0 and size of S
if v[x] true:
v[x] = false
return
Both of these seem pretty inefficient, especially if the sizes of U and S are large, and the difference between U and S is also large. Is there a way that I can perform both of these operations efficiently with only one data structure? or is there not really a big problem with maintaining two copies of the same thing?
EDIT:
The code I am writing is in c++, so I guess I am asking about c++ data structures in particular, but the question isn't really language specific.