In the previous post I modeled this logic puzzle in Clojure. The end of Alex's post suggested some follow-up exploration, which we'll take a look at below.
Let's insert a (remove-unique +)
at the beginning:
(->> (candidates 100)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(remove-unique *)
(remove-unique +)
(unique *))
((77 84))
Same answer. Sandy speaking first doesn't add any information since she's only able to rule out candidates that Peter can eliminate without her help. In fact, at the start, Sandy can only eliminate four candidates:
(->> (candidates 100)
(unique +))
((98 99) (99 99) (1 2) (1 1))
while Peter is able to eliminate too many to name:
(->> (candidates 100)
(unique *)
count)
1765
including the four Sandy can eliminate:
(->> (candidates 100)
(unique *)
(filter (->> (candidates 100) (unique +) set)))
((1 1) (98 99) (99 99) (1 2))
(defn difference [a b]
(Math/abs (- a b)))
(->> (candidates 100)
(unique difference))
((1 99))
There's only one unique absolute difference at the start, but it's not a pair Sandy could rule out, nor could Peter:
(->> (candidates 100)
(unique *)
(filter (->> (candidates 100) (unique difference) set)))
()
Hoping this new player will add some information, let's write a function to determine the process of finding an expected answer:
(defn find-answer [candidates fns answer]
(loop [candidates candidates
fns (take 60 (cycle fns))
didn't-know 0]
(if-let [[f & fns] (seq fns)]
(let [func (eval f)
uniques (unique func candidates)]
(if (and (= 1 (count uniques))
(= answer (first uniques)))
{:didn't-know didn't-know :knows f}
(recur (remove-unique func candidates)
fns
(inc didn't-know))))
:failed)))
Testing on our original problem:
(find-answer (candidates 100) '[* +] [77 84])
{:didn't-know 14, :knows *}
multiplying-the-expected-value-of-a-uniform-distribution in difference
:
(find-answer (candidates 100) '[* + difference] [77 84])
{:didn't-know 21, :knows *}
(find-answer (candidates 100) '[* difference +] [77 84])
{:didn't-know 21, :knows *}
(find-answer (candidates 100) '[difference * +] [77 84])
{:didn't-know 22, :knows *}
Looks like adding a player who knows the absolute difference between the random numbers doesn't help either. To see whether they fall into a problem like Sandy's when going first, or whether the third player just eliminates candidates without getting Peter closer to the right answer, let's adapt the loop above to track how many unique candidates each player is able to eliminate:
(defn track-uniques [candidates fns answer]
(loop [candidates candidates
fns (take 60 (cycle fns))
unique-candidates []]
(if-let [[f & fns] (seq fns)]
(let [func (eval f)
uniques (unique func candidates)]
(if (and (= 1 (count uniques))
(= answer (first uniques)))
unique-candidates
(recur (remove-unique func candidates)
fns
(conj unique-candidates [f (count uniques)]))))
:failed)))
(track-uniques (candidates 100) '[* + difference] [77 84])
[[* 1765] [+ 9] [difference 1] [* 3] [+ 2] [difference 0] [* 2] [+ 1] [difference 0] [* 1] [+ 1] [difference 0] [* 1] [+ 1] [difference 0] [* 1] [+ 1] [difference 0] [* 1] [+ 1] [difference 0]]
After the first round, the third player isn't able to eliminate any candidates that haven't already been eliminated.
I'm not sure this question makes sense, since the order of the randomly chosen numbers isn't meaningful. In fact, if we were to preserve the order of the candidate pairs, subtracting one number from another would produce different results for the same pair of numbers, such as 1 - 98 = -97 versus 98 - 1 = 97, which is a difference without a distinction.