Reputation: 183
I have a function that is a list of structs called Sandbox. See below:
defmodule Sandbox do
defstruct description: nil, chance: nil
def all, do: [
%Sandbox{
description: "Description 1",
chance: [1..40]
},
%Sandbox{
description: "Description 2",
chance: [41..60]
},
%Sandbox{
description: "Description 3",
chance: [61..100]
},
]
I want to select a Sandbox at random with a specific probability for each Sandbox. My plan is to assign a range of numbers to an attribute called "chance" based upon the desired probability for each item, as seen above. I will then generate a number at random from 1 to 100 and match it with each Sandboxes list to find the winning Sandbox. The function needs to return the winning Sandbox.
Questions:
Can you write out a function to perform the match and return the proper Sandbox? Have been trying various combinations for a while and can't seem to figure it out.
Can anyone think of an easier way to do this? I feel I've overcomplicated this problem.
Really appreciate the help.
Upvotes: 1
Views: 311
Reputation: 4885
You may be running into issues because the chance
is a range
inside a list
.
This should do what you're looking for:
defmodule Sandbox do
defstruct description: nil, chance: nil
def all, do: [
%Sandbox{
description: "Description 1",
chance: 1..40
},
%Sandbox{
description: "Description 2",
chance: 41..60
},
%Sandbox{
description: "Description 3",
chance: 61..100
},
]
@doc "Select a sandbox given a random value between 1 and 100"
def find(r) do
all() |> Enum.find(fn s -> r in s.chance end)
end
@doc "Select a sandbox at random"
def random() do
:rand.uniform(100) |> find()
end
end
Upvotes: 2
Reputation: 222198
First of all you shouldn't wrap the range in a list. Just write chance: 1..40
, chance: 41..60
, etc. Here's how you can generate a random integer and then select the correct sandbox:
defmodule Sandbox do
defstruct description: nil, chance: nil
def all, do: [
%Sandbox{
description: "Description 1",
chance: 1..40
},
%Sandbox{
description: "Description 2",
chance: 41..60
},
%Sandbox{
description: "Description 3",
chance: 61..100
},
]
def random do
rand = :rand.uniform(100)
Enum.find(all(), fn %{chance: chance} -> rand in chance end)
end
end
Can anyone think of an easier way to do this? I feel I've overcomplicated this problem.
This approach looks good to me. If performance is a concern it should be faster to store the list in a tree-like data structure where we can jump to the right chance
faster than iterating through each element.
Upvotes: 3