3

I have a list of keys in a dictionary, and I would like to assign a number between 0 and 1 to each item in such a way that the assigned numbers sum to 1. How would one do this? I tried something with rand(), but that didn't work. Any suggestions?

graphtheory123
  • 311
  • 1
  • 6
  • 1
    Does this answer your question? [Getting N random numbers whose sum is M](https://stackoverflow.com/questions/2640053/getting-n-random-numbers-whose-sum-is-m) – Peter O. Sep 19 '21 at 22:15

2 Answers2

4

The more mathematical answer is to use a Dirichlet distribution. Specifically, a symmetric Dirichlet with concentration parameter 1.0 is uniform on the N-simplex:

julia> using Distributions

julia> d = rand(Dirichlet(4, 1.0))
4-element Vector{Float64}:
 0.1629111431248381
 0.025227624238719375
 0.030334086399317672
 0.7815271462371249

julia> sum(d)
1.0
phipsgabler
  • 20,535
  • 4
  • 40
  • 60
2

You can generate a "probability vector", i.e. a vector of length d whose entries are all non-negative and sum to 1, uniformly at random by:

using Random

rand_probvec(d) = rand_probvec(Random.GLOBAL_RNG, d)

function rand_probvec(rng, d)
    unif = rand(rng, d - 1)
    T = eltype(unif)
    w = zeros(T, d + 1)
    w[2:d] .= sort(unif)
    w[d+1] = one(T)
    return diff(w)
end

following http://www.cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf.

If you don't care if your distribution is drawn uniformly at random, you could just draw d random numbers and divide by their sum, e.g.

function rand_probvec2(d)
    v = rand(d)
    return v / sum(v)
end

Either way, once you can generate random probability vectors, if you have a list of elements you wish to assign the probabilities to, you can do something like

my_list = ["a", "b", "c"]
prob_vec = rand_probvec(length(my_list))
my_dist = Dict(k => prob_vec[i] for (i, k) in enumerate(my_list))
Eric
  • 426
  • 3
  • 6