I had the same problem. Given a set where each item has a probability and whose items' probabilities sum up to one, I wanted to draw a sample efficiently, i.e. without sorting anything and without repeatedly iterating over the set.
The following function draws the lowest of N uniformly distributed random numbers within the interval [a,1). Let r be a random number from [0,1).
next(N,a)=1−(1−a)⋅r√N
You can use this function to draw an ascending series (ai) of N uniformly distributed random numbers in [0,1). Here is an example with N=10:
a0=next(10,0)
a1=next(9,a0)
a2=next(8,a1)
…
a9=next(1,a8)
While drawing that ascending series (ai) of uniformly distributed numbers, iterate over the set of probabilities P which represents your arbitraty (yet finite) distribution. Let 0≤k<|P| be the iterator and pk∈P. After drawing ai, increment k zero or more times until ∑p0…pk>ai. Then add pk to your sample and move on with drawing ai+1.
Example with the op's set {(1,0.04),(2,0.5),(3,0.46)} and sample size N=10:
i a_i k Sum Draw
0 0.031 0 0.04 1
1 0.200 1 0.54 2
2 0.236 1 0.54 2
3 0.402 1 0.54 2
4 0.488 1 0.54 2
5 0.589 2 1.0 3
6 0.625 2 1.0 3
7 0.638 2 1.0 3
8 0.738 2 1.0 3
9 0.942 2 1.0 3
Sample: (1,2,2,2,2,3,3,3,3,3)
If you wonder about the next function: It is the inverse of the probability that one of N uniformly distributed random numbers lies within the interval [a,x) with x≤1.