Simulating a probability problem: 3 independent dice - julia

I decided to simulate a probability question from a textbook:
Three fair dice are rolled independently, what is the probability that one dice shows 6 and the other two show two non-equal numbers (and neither is equal 6)
The dice is assumed fair, so "theoretical" answer will be $\frac{\binom{5}{2}}{\binom{6}{3}}=0.5$; I decided to simulate this in Julia, here is the function I wrote for this:
function simulation(n_experiments::Int = 100)
dice = [DiscreteUniform(1, 6) for i in 1:3] # 3 independent fair dice
successes = 0
for trial in 1:n_experiments
experiment = rand.(dice) # roll
one_six = sum(r == 6 for r in experiment) == 1 # check if there is only one "6"
others = filter(num -> num != 6, experiment)
no_duplicates = length(Set(others)) == 2 # check if other two are distinct
if no_duplicates
if one_six
successes += 1 # count "success"
end
end
end
return successes / n_experiments
end
I expected this to return something around 0.5, but in fact it returns ~0.27 after about 10^5 iterations (n_experiments). Can anyone please help me find the flaw in the code?

I think your code is right, but your math wrong:
julia> function simulation(n_experiments=100)
chains = rand.(fill(DiscreteUniform(1, 6), 3), n_experiments)
return (1/n_experiments) * mapreduce(+, chains...) do d1, d2, d3
(d1 == 6 && d2 != 6 && d3 != 6 && d2 != d3) || (d1 != 6 && d2 == 6 && d3 != 6 && d1 != d3) || (d1 != 6 && d2 != 6 && d3 == 6 && d1 != d2)
end
end
simulation (generic function with 1 method)
julia> simulation(10_000)
0.279
julia> count(Iterators.product(1:6, 1:6, 1:6)) do (d1, d2, d3)
(d1 == 6 && d2 != 6 && d3 != 6 && d2 != d3) || (d1 != 6 && d2 == 6 && d3 != 6 && d1 != d3) || (d1 != 6 && d2 != 6 && d3 == 6 && d1 != d2)
end
60
julia> 60 / 6^3
0.2777777777777778
I'd love to see a concise way to encode the condition. I avoided your variant due to allocations, and mine is only verbose make sure it's correct -- but way too long...
Addendum
Here's the optimized variant with a generated condition. Not relevant for the question, but because I find it cool. Credit for the condition formula goes to Bogumił.
julia> #generated function condition(xs::NTuple{N,<:Any}) where {N}
offdiagonals = ((i, j) for i = 1:N for j = (i+1):N)
names = Symbol.(:xs, 1:N)
assignments = [:($(names[i]) = xs[$i]) for i = 1:N]
comparisons = [:($(names[i]) != $(names[j])) for (i, j) in offdiagonals]
condition = Expr(:&&, :(maximum(xs) == 6), comparisons...)
return quote
$(assignments...)
$condition
end
end
condition (generic function with 1 method)
julia> #code_warntype condition((1,2,3))
MethodInstance for condition(::Tuple{Int64, Int64, Int64})
from condition(xs::Tuple{Vararg{var"#s17", N}} where var"#s17") where N in Main at REPL[71]:1
Static Parameters
N = 3
Arguments
#self#::Core.Const(condition)
xs::Tuple{Int64, Int64, Int64}
Locals
xs3::Int64
xs2::Int64
xs1::Int64
Body::Bool
1 ─ (xs1 = Base.getindex(xs, 1))
│ (xs2 = Base.getindex(xs, 2))
│ (xs3 = Base.getindex(xs, 3))
│ %4 = Main.maximum(xs)::Int64
│ %5 = (%4 == 6)::Bool
└── goto #7 if not %5
2 ─ %7 = (xs1 != xs2)::Bool
└── goto #6 if not %7
3 ─ %9 = (xs1 != xs3)::Bool
└── goto #5 if not %9
4 ─ %11 = (xs2 != xs3)::Bool
└── return %11
5 ─ return false
6 ─ return false
7 ─ return false
julia> function simulation(n_experiments=100)
(1/n_experiments) * sum(1:n_experiments) do _
dice = (rand(1:6), rand(1:6), rand(1:6))
condition(dice)
end
end
simulation (generic function with 2 methods)
julia> #time simulation(10_000_000)
0.157303 seconds
0.2777498
This has no allocations at all. sum is exactly as fast as a manual loop!

Related

How does one elegantly initialize start values or parameter values when creating a julia JuMP parameter or variable container?

Was hoping a Julia or JuMP expert could help me on how to do the following (apologies as I am new to the language and tried all resources but cannot find a solution to this problem)
I am trying to initialize the start values of all variables when creating a JuMP variable container.
I have a function defined as follows:
function sam(i, j)
if i == "BRD" && j == "CAP"
return 5
elseif i == "BRD" && j == "LAB"
return 10
elseif i == "MLK" && j == "CAP"
return 20
elseif i == "MLK" && j == "LAB"
return 15
elseif i == "CAP" && j == "HOH"
return 25
elseif i == "HOH" && j == "BRD"
return 15
elseif i == "HOH" && j == "MLK"
return 35
else
return nothing
end
end
I want to initialize a couple model variables, let's say household consumption of a product.
#variable(model, 0.001 <= Xᵢ[h, g]) # where h = ["HOH"] and g = ["BRD", "MLK"]
This variable can be initialized using the function above. It should be "HOH" -> "BRD" and "HOH" -> "MLK". So 15 and 35 respectively.
I can do these initializations by doing this:
set_start_value(Xᵢ["HOH", "BRD"], sam("HOH", "BRD"))
set_start_value(Xᵢ["HOH", "MLK"], sam("HOH", "MLK"))
However, I was hoping there would be a better way to do this using the start option. I have tried the following with no success.
#variable(model, 0.001 <= Xᵢ[h, g], start = sam(h, g)) # option 1
#variable(model, 0.001 <= Xᵢ[h, g], start = sam.(h, g)) # option 2
#variable(model, 0.001 <= Xᵢ[h, g], start = sam.(permute(h), g)) # option 3
#variable(model, 0.001 <= Xᵢ[h, g], start = [sam(h,g) for h in h, for g in g]) # option 4
The same question similarly applies to the creation of #NLparameter`. How can one do this for a parameter. Options tried below:
#NLparameter(model, 0.001 <= FFᶠ[f][h] == sam(f, h))
#NLparameter(model, 0.001 <= FFᶠ[f][h] == sam.(f, h))
#NLparameter(model, 0.001 <= FFᶠ[f][h] == sam.(permute(f), h))
#NLparameter(model, 0.001 <= FFᶠ[f][h] == [sam(f,h) for f in f, for h in h])
A simpler example:
I have some function f = x^2 + y^2 and arrays x=[1,2,3,4,5,6] and y=[1,2,3,4,5,6] how can I write:
#variable(model, v[x,y], start=f(x,y))
Such that value(v[1, 2]) start value will be equal to 1^2 + 2^2 therefore 5.
Do something like:
julia> using JuMP
julia> h, g = ["HOH"], ["BRD", "MLK"]
(["HOH"], ["BRD", "MLK"])
julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.
julia> function sam(i, j)
if i == "HOH" && j == "BRD"
return 15
else
return nothing
end
end
sam (generic function with 1 method)
julia> #variable(model, 0.001 <= X[i=h, j=g], start = sam(i, j))
2-dimensional DenseAxisArray{VariableRef,2,...} with index sets:
Dimension 1, ["HOH"]
Dimension 2, ["BRD", "MLK"]
And data, a 1×2 Matrix{VariableRef}:
X[HOH,BRD] X[HOH,MLK]
julia> start_value.(X)
2-dimensional DenseAxisArray{Union{Nothing, Float64},2,...} with index sets:
Dimension 1, ["HOH"]
Dimension 2, ["BRD", "MLK"]
And data, a 1×2 Matrix{Union{Nothing, Float64}}:
15.0 nothing
I guess we could make this more explicit in the documentation. I've opened an issue to get this fixed: https://github.com/jump-dev/JuMP.jl/issues/3147

Problems with implementing return on recursive function with Julia

I have implemented the next code for going down and right in a grid, and I am trying to implement some sort of counting like return +1 but I can't, if I'm forced to use global it makes the runtime considerably longer (like times 8 though it varies with the number of iterations).
Please advise on how to code correctly in this language.
I also thought about using pointers like in C but had hard time implementing it
function numOfPaths(NominalSize,x,y)
(x < NominalSize) && numOfPaths(NominalSize,x+1,y)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1)
(x >= NominalSize && y>=NominalSize) && global count+=1;
end
count = 0;
t1 = #elapsed numOfPaths(16,0,0)
2nd version with Ref instead of using global, pretty much same horrible performance.
function numOfPaths(NominalSize,x,y)
(x < NominalSize) && numOfPaths(NominalSize,x+1,y)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1)
((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
end
count = 0;
ref = Ref(count)
t1 = #elapsed anse = numOfPaths(15,0,0)
With the current state of the Julia compiler I think you have two options (assuming you do not change the logic of your code).
The first one is leave things as they are in your code, but change count to be a global const. However, as you want to mutate it you have to introduce some wrapper, a natural one is Ref. So you can go with something like:
const count = Ref(0)
function numOfPaths(NominalSize,x,y)
(x < NominalSize) && numOfPaths(NominalSize,x+1,y)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1)
(x >= NominalSize && y>=NominalSize) && global (count[] +=1)
end
count[] = 0
#time numOfPaths(16,0,0)
count[]
Now you could pass around count like this:
function numOfPaths(NominalSize,x,y, count=Ref(0))
(x < NominalSize) && numOfPaths(NominalSize,x+1,y, count)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1, count)
(x >= NominalSize && y>=NominalSize) && (count[] +=1)
return count[]
end
as a parameter, but it will be a bit slower.
Finally (and this is an option that is a bit faster than the first one and is recommended) you can perform the accumulation of count without passing it as an argument (this time it will be just an integer):
function numOfPaths(NominalSize,x,y)
count = 0
x < NominalSize && (count += numOfPaths(NominalSize,x+1,y))
y < NominalSize && (count += numOfPaths(NominalSize,x,y+1))
x >= NominalSize && y>=NominalSize && (count+=1)
return count
end
EDIT:
Additionally let me comment on what does not work. Naturally you would want to rewrite your function like this:
function numOfPaths(NominalSize,x,y)
function f(x, y)
x < NominalSize && f(x+1,y)
y < NominalSize && f(x,y+1)
x >= NominalSize && y>=NominalSize && (count+=1)
end
count = 0
f(x, y)
return count
end
Unfortunately, currently such code is slow, as Julia is boxing count:
julia> #code_warntype numOfPaths(16, 0, 0)
Variables
#self#::Core.Compiler.Const(numOfPaths, false)
NominalSize::Int64
x::Int64
y::Int64
f#_5::Core.Box
count#_6::Core.Box
f#_7::Union{}
f#_8::Union{}
count#_9::Union{}
Body::Any
1 ─ (f#_5 = Core.Box())
│ (count#_6 = Core.Box())
│ %3 = Main.:(var"#f#3")::Core.Compiler.Const(var"#f#3", false)
│ %4 = Core.typeof(NominalSize)::Core.Compiler.Const(Int64, false)
│ %5 = Core.apply_type(%3, %4)::Core.Compiler.Const(var"#f#3"{Int64}, false)
│ %6 = f#_5::Core.Box
│ %7 = %new(%5, NominalSize, %6, count#_6)::var"#f#3"{Int64}
│ Core.setfield!(f#_5, :contents, %7)
│ Core.setfield!(count#_6, :contents, 0)
│ %10 = Core.isdefined(f#_5, :contents)::Bool
└── goto #3 if not %10
2 ─ goto #4
3 ─ Core.NewvarNode(:(f#_8))
└── f#_8
4 ┄ %15 = Core.getfield(f#_5, :contents)::Any
│ (%15)(x, y)
│ %17 = Core.isdefined(count#_6, :contents)::Bool
└── goto #6 if not %17
5 ─ goto #7
6 ─ Core.NewvarNode(:(count#_9))
└── count#_9
7 ┄ %22 = Core.getfield(count#_6, :contents)::Any
└── return %22
Hopefully in the future this will get resolved (it is a known issue).
EDIT 2
First let me comment what to do if you wanted to make this code run faster. In this case instead of recursion one can use dynamic programming, e.g.:
function numOfPaths(NominalSize, x, y)
y, x = minmax(x, y)
a = ones(BigInt, NominalSize + 1 - y)
for i in 2:NominalSize - x + 1
i > length(a) || (a[i] = 2 * a[i])
for j in i+1:length(a)
a[j] += a[j-1]
end
end
return a[end]
end
(note that I am using BigInt here, as with this code you can test values that are much larger than the range of Int64 type; if you would want to be even faster just switch BigInt to Int in the code, but then for NominalSize greater than 33 you will get an overflow)
Now, going to the question of #Alex Zh why the second code runs slowly. You can run #code_warntype on it to learn the following:
julia> function numOfPaths(NominalSize,x,y)
(x < NominalSize) && numOfPaths(NominalSize,x+1,y)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1)
((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
end
numOfPaths (generic function with 1 method)
julia> count = 0;
julia> ref = Ref(count)
Base.RefValue{Int64}(0)
julia> #code_warntype numOfPaths(15,0,0)
Variables
#self#::Core.Compiler.Const(numOfPaths, false)
NominalSize::Int64
x::Int64
y::Int64
Body::Any
1 ─ %1 = (x < NominalSize)::Bool
└── goto #3 if not %1
2 ─ %3 = (x + 1)::Int64
│ Main.numOfPaths(NominalSize, %3, y)
└── goto #3
3 ┄ %6 = (y < NominalSize)::Bool
└── goto #5 if not %6
4 ─ %8 = (y + 1)::Int64
│ Main.numOfPaths(NominalSize, x, %8)
└── goto #5
5 ┄ %11 = (y >= NominalSize)::Bool
└── goto #9 if not %11
6 ─ %13 = (x >= NominalSize)::Bool
└── goto #8 if not %13
7 ─ %15 = Base.getindex(Main.ref)::Any
│ %16 = (%15 + 1)::Any
│ Base.setindex!(Main.ref, %16)
└── return %16
8 ─ return false
9 ─ return false
Now in the line:
%15 = Base.getindex(Main.ref)::Any
you see that when Julia fetches ref from Main it does not know what is its type, as it is a global variable that is not a constant. Therefore the compiler is not able to generate an efficient machine code, as the type of ref has to be resolved at run time (not at compile time).
Now consider the following change (you need to restart Julia to test it):
ulia> function numOfPaths(NominalSize,x,y)
(x < NominalSize) && numOfPaths(NominalSize,x+1,y)
(y < NominalSize) && numOfPaths(NominalSize,x,y+1)
((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
end
numOfPaths (generic function with 1 method)
julia> count = 0;
julia> const ref = Ref(count)
Base.RefValue{Int64}(0)
julia> #code_warntype numOfPaths(15,0,0)
Variables
#self#::Core.Compiler.Const(numOfPaths, false)
NominalSize::Int64
x::Int64
y::Int64
Body::Union{Bool, Int64}
1 ─ %1 = (x < NominalSize)::Bool
└── goto #3 if not %1
2 ─ %3 = (x + 1)::Int64
│ Main.numOfPaths(NominalSize, %3, y)
└── goto #3
3 ┄ %6 = (y < NominalSize)::Bool
└── goto #5 if not %6
4 ─ %8 = (y + 1)::Int64
│ Main.numOfPaths(NominalSize, x, %8)
└── goto #5
5 ┄ %11 = (y >= NominalSize)::Bool
└── goto #9 if not %11
6 ─ %13 = (x >= NominalSize)::Bool
└── goto #8 if not %13
7 ─ %15 = Base.getindex(Main.ref)::Int64
│ %16 = (%15 + 1)::Int64
│ Base.setindex!(Main.ref, %16)
└── return %16
8 ─ return false
9 ─ return false
Now I have made ref a global const. This tells the compiler that ref is guaranteed not to change its type. Therefore you have:
7 ─ %15 = Base.getindex(Main.ref)::Int64
as this time the compiler has all type information it needs, so a much more efficient machine code can be generated.
I found a Pkg that uses cache, runtime for 20 NominalSize is 0.0002673 [sec]
doing it without cache takes about 20 [min] for same size
so its Bogumił Kamiński answer + memoization
using Memoization
#memoize function numOfPaths(NominalSize,x,y)
count = 0
x < NominalSize && (count += numOfPaths(NominalSize,x+1,y))
y < NominalSize && (count += numOfPaths(NominalSize,x,y+1))
x >= NominalSize && y>=NominalSize && (count+=1)
return count
end
t = #elapsed paths = numOfPaths(20,0,0)

Scilab - How to compare between numbers

Assume
s1 = 'Yes';
s2 = 'No';
tf = strcmp(s1,s2)
If not equal, it will provide 0 else 1.
The following above example is to compare between strings. But I can't do the same with numbers.
You can simply use the comparison operators for numbers:
n1 = 1;
n2 = 2;
tf = n1 == n2
If you really want a 0 and a 1 for the false and the true, you can use
tf = bool2s(n1 == n2)

Vector as matrix coordinates

See https://stackoverflow.com/questions/41810306/appointment-scheduling....
You example does not work. You are indexing the second element twice (by the way, a nice alternative to your floor (rand (n) * x) is to use randi()):
octave> M = randi(10, 3)
M =
9 2 5
9 3 1
2 8 7
octave> v = [2;2];
octave> M(v)
ans =
9
9
octave> M([2;2])
ans =
9
9
The right way to do what you want is to use sub2ind() which works for any number of dimensions.
octave> M(sub2ind (size (M), 2, 2))
ans = 3
octave> M = randi (10, 3, 3, 3)
M =
ans(:,:,1) =
6 3 10
1 7 9
7 6 8
ans(:,:,2) =
7 9 10
9 4 5
8 5 5
ans(:,:,3) =
3 5 10
8 3 10
4 9 4
octave> M(sub2ind (size (M), 1, 2, 3))
ans = 5
I edited the sub2ind function so it can take a vector.
works like this:
M(sub2ind2(dims, V));
I will probably send the modified sub2ind2 function on the next days.
[EDIT]
function ind = sub2ind2 (dims, varargin)
if (nargin > 1)
if (isvector (dims) && all (round (dims) == dims))
nd = length (dims);
v = varargin{1};
vlen = length (v)
dims(vlen) = prod (dims(vlen:nd));
dims(vlen+1:nd) = [];
scale = cumprod (dims(:));
for i = 1:vlen
arg = v(i);
if (isnumeric (arg) && isequal (round (arg), arg))
if (i == 1)
if (all (arg(:) > 0 & arg(:) <= dims(i)))
ind = first_arg = arg;
else
error ("sub2ind: index out of range");
endif
else
if (size_equal (first_arg, arg))
if ((i > nd && arg == 1) || all (arg(:) > 0 & arg(:) <= dims(i)))
ind += scale(i-1) * (arg - 1);
else
error ("sub2ind: index out of range");
endif
else
error ("sub2ind: all index arguments must be the same size");
endif
endif
else
error ("sub2ind: expecting integer-valued index arguments");
endif
endfor
else
error ("sub2ind: expecting dims to be an integer vector");
endif
else
print_usage ();
endif
endfunction

2 Dimentional array dereferencing ,how to evaluate through pointers

a[2][3] = {{-3,14,5},{1,-10,8}}
*(a[j]+k)
*(a[j+k-2])
(*(a+j))[k])
(*(a+k-1))[j]
*((*(a+j))+k))
(**(a+j)+k)
*(&a[0][0]+j+k)
when i printf these i get
Output:
8
1
8
-10
8
3
1
respectively
Please if anyone can explain in detail how the values are coming ,i am a new starter please pardon me for bad formatting here and also bothering you for with so much work :)
I assume j == 1 and k == 2:
*(a[j]+k) == *(a[1]+2) :
a[1] = {1, -10, 8};
So a[1]+2 is a pointer to 8, and *(a[1]+2) == 8
*(a[j+k-2]) == *(a[1+2-2]) == *(a[1]):
a[1] = {1, -10, 8}
Since *a[1] is the value of the first element in a[1], the expression evaluates to 1
(*(a+j))[k] == (*(a+1))[2]:
a+1 is a pointer to the second element in a, so *(a+1) == a[1] = {1, -10, 8}
a[1][2] == 8
(*(a+k-1))[j] == (*(a+2-1))[1] == (*(a+1))[1]:
*(a+1) == a[1] (see the last answer)
a[1][1] == -10
*((*(a+j))+k) == *((*(a+1))+2):
*(a+1) == a[1], so the expressions is equivalent to *(a[1]+2)
*(a[1]+2) is equivalent to a[1][2] for the same reasoning as above, which is 8
(**(a+j)+k) == (**(a+1)+2):
*(a+1) = a[1]
**(a+1) = a[1][0] == 1
Adding 2 to that gives 3
*(&a[0][0]+j+k) == *(&a[0][0]+1+2) == *(&a[0][0]+3):
&a[0][0] == a[0]
*(a[0]+3) == a[0][3];
This last one is returning 1, because it is extending in memory past the end of a[0] into a[1], and you're getting a[1][0]. I think this behavior is actually undefined though. It depends on whether the C standard guarantees that an initialized 2D array will be allocated sequentially in memory or not, and could result in a Segmentation fault or worse.

Resources